git_iris/
instruction_presets.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Serialize, Deserialize, Clone)]
5pub struct InstructionPreset {
6    pub name: String,
7    pub description: String,
8    pub instructions: String,
9    pub emoji: String,           // New field for emoji
10    pub preset_type: PresetType, // New field to distinguish between commit and review presets
11}
12
13#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, Default)]
14pub enum PresetType {
15    Commit,
16    Review,
17    #[default]
18    Both,
19}
20
21#[derive(Debug, Serialize, Deserialize)]
22pub struct InstructionPresetLibrary {
23    presets: HashMap<String, InstructionPreset>,
24}
25
26impl Default for InstructionPresetLibrary {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl InstructionPresetLibrary {
33    #[allow(clippy::too_many_lines)]
34    pub fn new() -> Self {
35        let mut presets = HashMap::new();
36
37        presets.insert(
38            "default".to_string(),
39            InstructionPreset {
40                name: "Default".to_string(),
41                description: "Standard professional style".to_string(),
42                instructions: "Provide clear, concise, and professional responses. Focus on accuracy and relevance.".to_string(),
43                emoji: "๐Ÿ“".to_string(),
44                preset_type: PresetType::Both,
45            },
46        );
47
48        presets.insert(
49            "detailed".to_string(),
50            InstructionPreset {
51                name: "Detailed".to_string(),
52                description: "Provide more context and explanation".to_string(),
53                instructions: "Offer comprehensive explanations, including background information, potential impacts, and related considerations. Aim for thoroughness while maintaining clarity.".to_string(),
54                emoji: "๐Ÿ”".to_string(),
55                preset_type: PresetType::Both,
56            },
57        );
58
59        presets.insert(
60            "concise".to_string(),
61            InstructionPreset {
62                name: "Concise".to_string(),
63                description: "Short and to-the-point responses".to_string(),
64                instructions: "Keep responses brief and focused on the core information. Prioritize essential details and avoid unnecessary elaboration.".to_string(),
65                emoji: "๐ŸŽฏ".to_string(),
66                preset_type: PresetType::Both,
67            },
68        );
69
70        presets.insert(
71            "technical".to_string(),
72            InstructionPreset {
73                name: "Technical".to_string(),
74                description: "Focus on technical details".to_string(),
75                instructions: "Emphasize technical aspects in your responses. Include specific terminology, methodologies, or performance impacts where relevant. Assume a technically proficient audience.".to_string(),
76                emoji: "โš™๏ธ".to_string(),
77                preset_type: PresetType::Both,
78            },
79        );
80
81        presets.insert(
82            "storyteller".to_string(),
83            InstructionPreset {
84                name: "Storyteller".to_string(),
85                description: "Frame information as part of an ongoing narrative".to_string(),
86                instructions: "Present information as if it's part of a larger story. Use narrative elements to describe changes, developments, or features. Connect individual elements to create a cohesive narrative arc.".to_string(),
87                emoji: "๐Ÿ“š".to_string(),
88                preset_type: PresetType::Both,
89            },
90        );
91
92        presets.insert(
93            "emoji-lover".to_string(),
94            InstructionPreset {
95                name: "Emoji Enthusiast".to_string(),
96                description: "Use emojis to enhance communication".to_string(),
97                instructions: "Incorporate relevant emojis throughout your responses to add visual flair and quickly convey the nature of the information. Ensure emojis complement rather than replace clear communication.".to_string(),
98                emoji: "๐Ÿ˜".to_string(),
99                preset_type: PresetType::Both,
100            },
101        );
102
103        presets.insert(
104            "formal".to_string(),
105            InstructionPreset {
106                name: "Formal".to_string(),
107                description: "Maintain a highly professional and formal tone".to_string(),
108                instructions: "Use formal language and structure in your responses. Avoid colloquialisms and maintain a respectful, business-like tone throughout.".to_string(),
109                emoji: "๐ŸŽฉ".to_string(),
110                preset_type: PresetType::Both,
111            },
112        );
113
114        presets.insert(
115            "explanatory".to_string(),
116            InstructionPreset {
117                name: "Explanatory".to_string(),
118                description: "Focus on explaining concepts and changes".to_string(),
119                instructions: "Prioritize explaining the 'why' behind information or changes. Provide context, rationale, and potential implications to foster understanding.".to_string(),
120                emoji: "๐Ÿ’ก".to_string(),
121                preset_type: PresetType::Both,
122            },
123        );
124
125        presets.insert(
126            "user-focused".to_string(),
127            InstructionPreset {
128                name: "User-Focused".to_string(),
129                description: "Emphasize user impact and benefits".to_string(),
130                instructions: "Frame information in terms of its impact on users or stakeholders. Highlight benefits, improvements, and how changes affect the user experience.".to_string(),
131                emoji: "๐Ÿ‘ฅ".to_string(),
132                preset_type: PresetType::Both,
133            },
134        );
135
136        presets.insert(
137            "cosmic".to_string(),
138            InstructionPreset {
139                name: "Cosmic Oracle".to_string(),
140                description: "Channel mystical and cosmic energy".to_string(),
141                instructions: "Envision yourself as a cosmic entity, peering into the vast expanse of possibilities. Describe information as if they are celestial events or shifts in the fabric of reality. Use mystical and space-themed language to convey the essence and impact of each element.".to_string(),
142                emoji: "๐Ÿ”ฎ".to_string(),
143                preset_type: PresetType::Both,
144            },
145        );
146
147        presets.insert(
148            "academic".to_string(),
149            InstructionPreset {
150                name: "Academic".to_string(),
151                description: "Scholarly and research-oriented style".to_string(),
152                instructions: "Adopt an academic tone, citing relevant sources or methodologies where applicable. Use precise language and maintain a formal, analytical approach to the subject matter.".to_string(),
153                emoji: "๐ŸŽ“".to_string(),
154                preset_type: PresetType::Both,
155            },
156        );
157
158        presets.insert(
159            "comparative".to_string(),
160            InstructionPreset {
161                name: "Comparative".to_string(),
162                description: "Highlight differences and similarities".to_string(),
163                instructions: "Focus on comparing and contrasting elements. Identify key differences and similarities, and explain their significance or implications.".to_string(),
164                emoji: "โš–๏ธ".to_string(),
165                preset_type: PresetType::Both,
166            },
167        );
168
169        presets.insert(
170            "future-oriented".to_string(),
171            InstructionPreset {
172                name: "Future-Oriented".to_string(),
173                description: "Emphasize future implications and possibilities".to_string(),
174                instructions: "Frame information in terms of its future impact. Discuss potential developments, long-term consequences, and how current changes might shape future scenarios.".to_string(),
175                emoji: "๐Ÿ”ฎ".to_string(),
176                preset_type: PresetType::Both,
177            },
178        );
179
180        presets.insert(
181            "time-traveler".to_string(),
182            InstructionPreset {
183                name: "Time Traveler".to_string(),
184                description: "Narrate from different points in time".to_string(),
185                instructions: "Imagine you're a time traveler, jumping between past, present, and future. Describe current information as if you're reporting from different time periods. Use appropriate historical or futuristic language and references, and highlight how perspectives change across time.".to_string(),
186                emoji: "โณ".to_string(),
187                preset_type: PresetType::Both,
188            },
189        );
190
191        presets.insert(
192            "chef-special".to_string(),
193            InstructionPreset {
194                name: "Chef's Special".to_string(),
195                description: "Present information as a culinary experience".to_string(),
196                instructions: "Treat the information as ingredients in a gourmet meal. Describe changes or updates as if you're crafting a recipe or presenting a dish. Use culinary terms, cooking metaphors, and sensory descriptions to make the content more flavorful and engaging.".to_string(),
197                emoji: "๐Ÿ‘ฉโ€๐Ÿณ".to_string(),
198                preset_type: PresetType::Both,
199            },
200        );
201
202        presets.insert(
203            "superhero-saga".to_string(),
204            InstructionPreset {
205                name: "Superhero Saga".to_string(),
206                description: "Frame information in a superhero universe".to_string(),
207                instructions: "Imagine the project or product as a superhero universe. Describe features, changes, or updates as if they're superpowers, epic battles, or heroic adventures. Use dramatic, comic-book style language and frame developments in terms of heroes, villains, and saving the day.".to_string(),
208                emoji: "๐Ÿฆธ".to_string(),
209                preset_type: PresetType::Both,
210            },
211        );
212
213        presets.insert(
214            "nature-documentary".to_string(),
215            InstructionPreset {
216                name: "Nature Documentary".to_string(),
217                description: "Narrate as if observing a natural phenomenon".to_string(),
218                instructions: "Channel your inner David Attenborough and describe the information as if you're narrating a nature documentary. Treat code, features, or processes as flora and fauna in a complex ecosystem. Use a tone of fascination and wonder, and explain interactions and developments as if observing them in their natural habitat.".to_string(),
219                emoji: "๐ŸŒฟ".to_string(),
220                preset_type: PresetType::Both,
221            },
222        );
223
224        presets.insert(
225            "chill".to_string(),
226            InstructionPreset {
227                name: "Chill".to_string(),
228                description: "Professional but fun commit messages".to_string(),
229                instructions: "Use a style that's professionally informative but with a touch of clever humor. Keep it light and engaging while still conveying the essential information.".to_string(),
230                emoji: "๐Ÿ˜Ž".to_string(),
231                preset_type: PresetType::Both,
232            }
233        );
234
235        presets.insert(
236            "hater".to_string(),
237            InstructionPreset {
238                name: "Hater".to_string(),
239                description: "Hyper-critical and brutally honest style".to_string(),
240                instructions: "Adopt a hyper-critical approach. Focus on finding flaws, weaknesses, and potential issues. Provide brutally honest feedback and don't hesitate to point out even minor imperfections.".to_string(),
241                emoji: "๐Ÿ’ข".to_string(),
242                preset_type: PresetType::Both,
243            },
244        );
245
246        presets.insert(
247            "conventional".to_string(),
248            InstructionPreset {
249                name: "Conventional Commits".to_string(),
250                description: "Follow the Conventional Commits specification".to_string(),
251                instructions: "STRICT CONVENTIONAL COMMITS SPECIFICATION - FOLLOW EXACTLY:\n\n\
252                               FORMAT: <type>[optional scope]: <description>\n\n\
253                               MANDATORY RULES:\n\
254                               1. NO EMOJIS - Conventional commits never use emojis\n\
255                               2. NO CAPITALIZATION of type or scope\n\
256                               3. Subject line MUST be 50 characters or less\n\
257                               4. Description MUST be in imperative mood (add, fix, update - NOT added, fixed, updated)\n\
258                               5. NO period at end of subject line\n\
259                               6. USE SCOPES when files relate to specific components/modules\n\n\
260                               SCOPE USAGE - STRONGLY PREFERRED:\n\
261                               - For API changes: feat(api): add user endpoint\n\
262                               - For UI changes: feat(ui): add login form\n\
263                               - For auth: fix(auth): handle expired tokens\n\
264                               - For database: feat(db): add user table migration\n\
265                               - For tests: test(auth): add login validation tests\n\
266                               - For config: chore(config): update database settings\n\
267                               - For docs: docs(readme): update installation steps\n\
268                               - For CLI: feat(cli): add new command option\n\
269                               - For build: build(deps): update dependency versions\n\
270                               - Analyze the changed files and pick the most relevant component\n\n\
271                               VALID TYPES (use ONLY these):\n\
272                               - feat: new feature for the user\n\
273                               - fix: bug fix for the user\n\
274                               - docs: changes to documentation\n\
275                               - style: formatting, missing semicolons, etc (no code change)\n\
276                               - refactor: code change that neither fixes bug nor adds feature\n\
277                               - perf: code change that improves performance\n\
278                               - test: adding missing tests or correcting existing tests\n\
279                               - build: changes that affect build system or external dependencies\n\
280                               - ci: changes to CI configuration files and scripts\n\
281                               - chore: other changes that don't modify src or test files\n\
282                               - revert: reverts a previous commit\n\n\
283                               SCOPE SELECTION RULES:\n\
284                               - Look at the file paths and identify the main component/module\n\
285                               - Use the most specific relevant scope (prefer 'auth' over 'api' if it's auth-specific)\n\
286                               - Common scopes: api, ui, auth, db, cli, config, deps, core, utils, tests\n\
287                               - If multiple unrelated components, omit scope or use broader one\n\n\
288                               BODY (optional):\n\
289                               - Separate from subject with blank line\n\
290                               - Wrap at 72 characters\n\
291                               - Explain what and why, not how\n\
292                               - Use imperative mood\n\n\
293                               BREAKING CHANGES:\n\
294                               - Add '!' after type/scope: feat(api)!: remove deprecated endpoints\n\
295                               - OR include 'BREAKING CHANGE:' in footer\n\n\
296                               EXAMPLES:\n\
297                               โœ“ feat(auth): add OAuth login\n\
298                               โœ“ fix(api): resolve timeout issue\n\
299                               โœ“ docs(readme): update contributing guidelines\n\
300                               โœ“ feat(ui)!: remove deprecated button component\n\
301                               โœ“ refactor(core): extract validation logic\n\
302                               โœ— Add user authentication (missing type and scope)\n\
303                               โœ— feat: Add user authentication (missing scope when relevant)\n\
304                               โœ— feat: adds user authentication (wrong mood)\n\
305                               โœ— ๐ŸŽ‰ feat(auth): add authentication (has emoji)".to_string(),
306                emoji: "๐Ÿ“".to_string(),
307                preset_type: PresetType::Both,
308            },
309        );
310
311        // Add review-specific presets
312        presets.insert(
313            "security".to_string(),
314            InstructionPreset {
315                name: "Security".to_string(),
316                description: "Focus on security vulnerabilities and best practices".to_string(),
317                instructions: "Prioritize identifying security vulnerabilities, including potential injection attacks, authentication issues, authorization flaws, data exposure risks, and insecure configurations. Suggest security best practices and hardening techniques relevant to the code changes.".to_string(),
318                emoji: "๐Ÿ”’".to_string(),
319                preset_type: PresetType::Review,
320            },
321        );
322
323        presets.insert(
324            "performance".to_string(),
325            InstructionPreset {
326                name: "Performance".to_string(),
327                description: "Analyze code for performance optimizations".to_string(),
328                instructions: "Focus on identifying performance bottlenecks, inefficient algorithms, unnecessary computations, memory leaks, and resource management issues. Suggest optimization strategies and performance best practices specific to the language and framework being used.".to_string(),
329                emoji: "โšก".to_string(),
330                preset_type: PresetType::Review,
331            },
332        );
333
334        presets.insert(
335            "architecture".to_string(),
336            InstructionPreset {
337                name: "Architecture".to_string(),
338                description: "Evaluate architectural patterns and design decisions".to_string(),
339                instructions: "Analyze the architectural patterns and design decisions in the code. Evaluate separation of concerns, coupling between components, adherence to design principles (SOLID, DRY, etc.), and overall system structure. Suggest improvements to enhance maintainability, scalability, and extensibility.".to_string(),
340                emoji: "๐Ÿ—๏ธ".to_string(),
341                preset_type: PresetType::Review,
342            },
343        );
344
345        presets.insert(
346            "testing".to_string(),
347            InstructionPreset {
348                name: "Testing".to_string(),
349                description: "Focus on test coverage and testing strategies".to_string(),
350                instructions: "Evaluate test coverage and testing strategies for the code changes. Identify areas lacking tests, suggest test cases for edge conditions, and recommend testing approaches appropriate for the code (unit tests, integration tests, property-based tests, etc.). Emphasize ways to improve test quality and maintainability.".to_string(),
351                emoji: "๐Ÿงช".to_string(),
352                preset_type: PresetType::Review,
353            },
354        );
355
356        presets.insert(
357            "maintainability".to_string(),
358            InstructionPreset {
359                name: "Maintainability".to_string(),
360                description: "Evaluate code for long-term maintenance".to_string(),
361                instructions: "Focus on aspects that affect long-term code maintainability, including readability, documentation quality, consistent naming conventions, code complexity, and technical debt. Suggest refactorings that would improve future maintenance efforts and knowledge transfer between team members.".to_string(),
362                emoji: "๐Ÿ”ง".to_string(),
363                preset_type: PresetType::Review,
364            },
365        );
366
367        presets.insert(
368            "conventions".to_string(),
369            InstructionPreset {
370                name: "Code Conventions".to_string(),
371                description: "Check adherence to language and project coding standards".to_string(),
372                instructions: "Analyze how well the code adheres to language-specific conventions, project style guides, and industry best practices. Identify inconsistencies in formatting, naming, documentation, and structure. Suggest adjustments to improve consistency and alignment with established patterns in the codebase.".to_string(),
373                emoji: "๐Ÿ“".to_string(),
374                preset_type: PresetType::Review,
375            },
376        );
377
378        Self { presets }
379    }
380
381    pub fn get_preset(&self, key: &str) -> Option<&InstructionPreset> {
382        self.presets.get(key)
383    }
384
385    pub fn list_presets(&self) -> Vec<(&String, &InstructionPreset)> {
386        self.presets.iter().collect()
387    }
388
389    pub fn list_presets_by_type(
390        &self,
391        preset_type: Option<PresetType>,
392    ) -> Vec<(&String, &InstructionPreset)> {
393        match preset_type {
394            Some(typ) => self
395                .presets
396                .iter()
397                .filter(|(_, preset)| preset.preset_type == typ)
398                .collect(),
399            None => self.list_presets(),
400        }
401    }
402
403    pub fn list_valid_presets_for_command(
404        &self,
405        command_type: PresetType,
406    ) -> Vec<(&String, &InstructionPreset)> {
407        self.presets
408            .iter()
409            .filter(|(_, preset)| {
410                preset.preset_type == command_type || preset.preset_type == PresetType::Both
411            })
412            .collect()
413    }
414}
415
416pub fn get_instruction_preset_library() -> InstructionPresetLibrary {
417    InstructionPresetLibrary::new()
418}
419
420pub fn list_presets_formatted(library: &InstructionPresetLibrary) -> String {
421    list_presets_formatted_by_type(library, None)
422}
423
424pub fn list_presets_formatted_by_type(
425    library: &InstructionPresetLibrary,
426    preset_type: Option<PresetType>,
427) -> String {
428    let mut presets: Vec<_> = library.list_presets_by_type(preset_type);
429    presets.sort_by(|a, b| {
430        if a.1.name == "Default" {
431            std::cmp::Ordering::Less
432        } else if b.1.name == "Default" {
433            std::cmp::Ordering::Greater
434        } else {
435            a.1.name.cmp(&b.1.name)
436        }
437    });
438
439    presets
440        .iter()
441        .map(|(key, preset)| {
442            let type_indicator = match preset.preset_type {
443                PresetType::Commit => "[C]",
444                PresetType::Review => "[R]",
445                PresetType::Both => "[B]",
446            };
447            format!(
448                "{} {} - {} - {} - {}",
449                type_indicator, key, preset.emoji, preset.name, preset.description
450            )
451        })
452        .collect::<Vec<String>>()
453        .join("\n")
454}
455
456pub fn list_valid_presets_for_command_formatted(
457    library: &InstructionPresetLibrary,
458    command_type: PresetType,
459) -> String {
460    let mut presets: Vec<_> = library.list_valid_presets_for_command(command_type);
461    presets.sort_by(|a, b| {
462        if a.1.name == "Default" {
463            std::cmp::Ordering::Less
464        } else if b.1.name == "Default" {
465            std::cmp::Ordering::Greater
466        } else {
467            a.1.name.cmp(&b.1.name)
468        }
469    });
470
471    presets
472        .iter()
473        .map(|(key, preset)| {
474            let type_indicator = match preset.preset_type {
475                PresetType::Commit => "[C]",
476                PresetType::Review => "[R]",
477                PresetType::Both => "[B]",
478            };
479            format!(
480                "{} {} - {} - {} - {}",
481                type_indicator, key, preset.emoji, preset.name, preset.description
482            )
483        })
484        .collect::<Vec<String>>()
485        .join("\n")
486}