Skip to main content

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    #[must_use]
35    pub fn new() -> Self {
36        let mut presets = HashMap::new();
37
38        presets.insert(
39            "default".to_string(),
40            InstructionPreset {
41                name: "Default".to_string(),
42                description: "Standard professional style".to_string(),
43                instructions: "Provide clear, concise, and professional responses. Focus on accuracy and relevance.".to_string(),
44                emoji: "๐Ÿ“".to_string(),
45                preset_type: PresetType::Both,
46            },
47        );
48
49        presets.insert(
50            "detailed".to_string(),
51            InstructionPreset {
52                name: "Detailed".to_string(),
53                description: "Provide more context and explanation".to_string(),
54                instructions: "Offer comprehensive explanations, including background information, potential impacts, and related considerations. Aim for thoroughness while maintaining clarity.".to_string(),
55                emoji: "๐Ÿ”".to_string(),
56                preset_type: PresetType::Both,
57            },
58        );
59
60        presets.insert(
61            "concise".to_string(),
62            InstructionPreset {
63                name: "Concise".to_string(),
64                description: "Short and to-the-point responses".to_string(),
65                instructions: "Keep responses brief and focused on the core information. Prioritize essential details and avoid unnecessary elaboration.".to_string(),
66                emoji: "๐ŸŽฏ".to_string(),
67                preset_type: PresetType::Both,
68            },
69        );
70
71        presets.insert(
72            "technical".to_string(),
73            InstructionPreset {
74                name: "Technical".to_string(),
75                description: "Focus on technical details".to_string(),
76                instructions: "Emphasize technical aspects in your responses. Include specific terminology, methodologies, or performance impacts where relevant. Assume a technically proficient audience.".to_string(),
77                emoji: "โš™๏ธ".to_string(),
78                preset_type: PresetType::Both,
79            },
80        );
81
82        presets.insert(
83            "storyteller".to_string(),
84            InstructionPreset {
85                name: "Storyteller".to_string(),
86                description: "Frame information as part of an ongoing narrative".to_string(),
87                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(),
88                emoji: "๐Ÿ“š".to_string(),
89                preset_type: PresetType::Both,
90            },
91        );
92
93        presets.insert(
94            "emoji-lover".to_string(),
95            InstructionPreset {
96                name: "Emoji Enthusiast".to_string(),
97                description: "Use emojis to enhance communication".to_string(),
98                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(),
99                emoji: "๐Ÿ˜".to_string(),
100                preset_type: PresetType::Both,
101            },
102        );
103
104        presets.insert(
105            "formal".to_string(),
106            InstructionPreset {
107                name: "Formal".to_string(),
108                description: "Maintain a highly professional and formal tone".to_string(),
109                instructions: "Use formal language and structure in your responses. Avoid colloquialisms and maintain a respectful, business-like tone throughout.".to_string(),
110                emoji: "๐ŸŽฉ".to_string(),
111                preset_type: PresetType::Both,
112            },
113        );
114
115        presets.insert(
116            "explanatory".to_string(),
117            InstructionPreset {
118                name: "Explanatory".to_string(),
119                description: "Focus on explaining concepts and changes".to_string(),
120                instructions: "Prioritize explaining the 'why' behind information or changes. Provide context, rationale, and potential implications to foster understanding.".to_string(),
121                emoji: "๐Ÿ’ก".to_string(),
122                preset_type: PresetType::Both,
123            },
124        );
125
126        presets.insert(
127            "user-focused".to_string(),
128            InstructionPreset {
129                name: "User-Focused".to_string(),
130                description: "Emphasize user impact and benefits".to_string(),
131                instructions: "Frame information in terms of its impact on users or stakeholders. Highlight benefits, improvements, and how changes affect the user experience.".to_string(),
132                emoji: "๐Ÿ‘ฅ".to_string(),
133                preset_type: PresetType::Both,
134            },
135        );
136
137        presets.insert(
138            "cosmic".to_string(),
139            InstructionPreset {
140                name: "Cosmic Oracle".to_string(),
141                description: "Channel mystical and cosmic energy".to_string(),
142                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(),
143                emoji: "๐Ÿ”ฎ".to_string(),
144                preset_type: PresetType::Both,
145            },
146        );
147
148        presets.insert(
149            "academic".to_string(),
150            InstructionPreset {
151                name: "Academic".to_string(),
152                description: "Scholarly and research-oriented style".to_string(),
153                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(),
154                emoji: "๐ŸŽ“".to_string(),
155                preset_type: PresetType::Both,
156            },
157        );
158
159        presets.insert(
160            "comparative".to_string(),
161            InstructionPreset {
162                name: "Comparative".to_string(),
163                description: "Highlight differences and similarities".to_string(),
164                instructions: "Focus on comparing and contrasting elements. Identify key differences and similarities, and explain their significance or implications.".to_string(),
165                emoji: "โš–๏ธ".to_string(),
166                preset_type: PresetType::Both,
167            },
168        );
169
170        presets.insert(
171            "future-oriented".to_string(),
172            InstructionPreset {
173                name: "Future-Oriented".to_string(),
174                description: "Emphasize future implications and possibilities".to_string(),
175                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(),
176                emoji: "๐Ÿ”ฎ".to_string(),
177                preset_type: PresetType::Both,
178            },
179        );
180
181        presets.insert(
182            "time-traveler".to_string(),
183            InstructionPreset {
184                name: "Time Traveler".to_string(),
185                description: "Narrate from different points in time".to_string(),
186                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(),
187                emoji: "โณ".to_string(),
188                preset_type: PresetType::Both,
189            },
190        );
191
192        presets.insert(
193            "chef-special".to_string(),
194            InstructionPreset {
195                name: "Chef's Special".to_string(),
196                description: "Present information as a culinary experience".to_string(),
197                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(),
198                emoji: "๐Ÿ‘ฉโ€๐Ÿณ".to_string(),
199                preset_type: PresetType::Both,
200            },
201        );
202
203        presets.insert(
204            "superhero-saga".to_string(),
205            InstructionPreset {
206                name: "Superhero Saga".to_string(),
207                description: "Frame information in a superhero universe".to_string(),
208                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(),
209                emoji: "๐Ÿฆธ".to_string(),
210                preset_type: PresetType::Both,
211            },
212        );
213
214        presets.insert(
215            "nature-documentary".to_string(),
216            InstructionPreset {
217                name: "Nature Documentary".to_string(),
218                description: "Narrate as if observing a natural phenomenon".to_string(),
219                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(),
220                emoji: "๐ŸŒฟ".to_string(),
221                preset_type: PresetType::Both,
222            },
223        );
224
225        presets.insert(
226            "chill".to_string(),
227            InstructionPreset {
228                name: "Chill".to_string(),
229                description: "Professional but fun commit messages".to_string(),
230                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(),
231                emoji: "๐Ÿ˜Ž".to_string(),
232                preset_type: PresetType::Both,
233            }
234        );
235
236        presets.insert(
237            "hater".to_string(),
238            InstructionPreset {
239                name: "Hater".to_string(),
240                description: "Hyper-critical and brutally honest style".to_string(),
241                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(),
242                emoji: "๐Ÿ’ข".to_string(),
243                preset_type: PresetType::Both,
244            },
245        );
246
247        presets.insert(
248            "conventional".to_string(),
249            InstructionPreset {
250                name: "Conventional Commits".to_string(),
251                description: "Follow the Conventional Commits specification".to_string(),
252                instructions: "STRICT CONVENTIONAL COMMITS SPECIFICATION - FOLLOW EXACTLY:\n\n\
253                               FORMAT: <type>[optional scope]: <description>\n\n\
254                               MANDATORY RULES:\n\
255                               1. NO EMOJIS - Conventional commits never use emojis\n\
256                               2. NO CAPITALIZATION of type or scope\n\
257                               3. Subject line MUST be 50 characters or less\n\
258                               4. Description MUST be in imperative mood (add, fix, update - NOT added, fixed, updated)\n\
259                               5. NO period at end of subject line\n\
260                               6. USE SCOPES when files relate to specific components/modules\n\n\
261                               SCOPE USAGE - STRONGLY PREFERRED:\n\
262                               - For API changes: feat(api): add user endpoint\n\
263                               - For UI changes: feat(ui): add login form\n\
264                               - For auth: fix(auth): handle expired tokens\n\
265                               - For database: feat(db): add user table migration\n\
266                               - For tests: test(auth): add login validation tests\n\
267                               - For config: chore(config): update database settings\n\
268                               - For docs: docs(readme): update installation steps\n\
269                               - For CLI: feat(cli): add new command option\n\
270                               - For build: build(deps): update dependency versions\n\
271                               - Analyze the changed files and pick the most relevant component\n\n\
272                               VALID TYPES (use ONLY these):\n\
273                               - feat: new feature for the user\n\
274                               - fix: bug fix for the user\n\
275                               - docs: changes to documentation\n\
276                               - style: formatting, missing semicolons, etc (no code change)\n\
277                               - refactor: code change that neither fixes bug nor adds feature\n\
278                               - perf: code change that improves performance\n\
279                               - test: adding missing tests or correcting existing tests\n\
280                               - build: changes that affect build system or external dependencies\n\
281                               - ci: changes to CI configuration files and scripts\n\
282                               - chore: other changes that don't modify src or test files\n\
283                               - revert: reverts a previous commit\n\n\
284                               SCOPE SELECTION RULES:\n\
285                               - Look at the file paths and identify the main component/module\n\
286                               - Use the most specific relevant scope (prefer 'auth' over 'api' if it's auth-specific)\n\
287                               - Common scopes: api, ui, auth, db, cli, config, deps, core, utils, tests\n\
288                               - If multiple unrelated components, omit scope or use broader one\n\n\
289                               BODY (optional):\n\
290                               - Separate from subject with blank line\n\
291                               - Wrap at 72 characters\n\
292                               - Explain what and why, not how\n\
293                               - Use imperative mood\n\n\
294                               BREAKING CHANGES:\n\
295                               - Add '!' after type/scope: feat(api)!: remove deprecated endpoints\n\
296                               - OR include 'BREAKING CHANGE:' in footer\n\n\
297                               EXAMPLES:\n\
298                               โœ“ feat(auth): add OAuth login\n\
299                               โœ“ fix(api): resolve timeout issue\n\
300                               โœ“ docs(readme): update contributing guidelines\n\
301                               โœ“ feat(ui)!: remove deprecated button component\n\
302                               โœ“ refactor(core): extract validation logic\n\
303                               โœ— Add user authentication (missing type and scope)\n\
304                               โœ— feat: Add user authentication (missing scope when relevant)\n\
305                               โœ— feat: adds user authentication (wrong mood)\n\
306                               โœ— ๐ŸŽ‰ feat(auth): add authentication (has emoji)".to_string(),
307                emoji: "๐Ÿ“".to_string(),
308                preset_type: PresetType::Both,
309            },
310        );
311
312        // Add review-specific presets
313        presets.insert(
314            "security".to_string(),
315            InstructionPreset {
316                name: "Security".to_string(),
317                description: "Focus on security vulnerabilities and best practices".to_string(),
318                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(),
319                emoji: "๐Ÿ”’".to_string(),
320                preset_type: PresetType::Review,
321            },
322        );
323
324        presets.insert(
325            "performance".to_string(),
326            InstructionPreset {
327                name: "Performance".to_string(),
328                description: "Analyze code for performance optimizations".to_string(),
329                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(),
330                emoji: "โšก".to_string(),
331                preset_type: PresetType::Review,
332            },
333        );
334
335        presets.insert(
336            "architecture".to_string(),
337            InstructionPreset {
338                name: "Architecture".to_string(),
339                description: "Evaluate architectural patterns and design decisions".to_string(),
340                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(),
341                emoji: "๐Ÿ—๏ธ".to_string(),
342                preset_type: PresetType::Review,
343            },
344        );
345
346        presets.insert(
347            "testing".to_string(),
348            InstructionPreset {
349                name: "Testing".to_string(),
350                description: "Focus on test coverage and testing strategies".to_string(),
351                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(),
352                emoji: "๐Ÿงช".to_string(),
353                preset_type: PresetType::Review,
354            },
355        );
356
357        presets.insert(
358            "maintainability".to_string(),
359            InstructionPreset {
360                name: "Maintainability".to_string(),
361                description: "Evaluate code for long-term maintenance".to_string(),
362                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(),
363                emoji: "๐Ÿ”ง".to_string(),
364                preset_type: PresetType::Review,
365            },
366        );
367
368        presets.insert(
369            "conventions".to_string(),
370            InstructionPreset {
371                name: "Code Conventions".to_string(),
372                description: "Check adherence to language and project coding standards".to_string(),
373                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(),
374                emoji: "๐Ÿ“".to_string(),
375                preset_type: PresetType::Review,
376            },
377        );
378
379        Self { presets }
380    }
381
382    #[must_use]
383    pub fn get_preset(&self, key: &str) -> Option<&InstructionPreset> {
384        self.presets.get(key)
385    }
386
387    #[must_use]
388    pub fn list_presets(&self) -> Vec<(&String, &InstructionPreset)> {
389        self.presets.iter().collect()
390    }
391
392    #[must_use]
393    pub fn list_presets_by_type(
394        &self,
395        preset_type: Option<PresetType>,
396    ) -> Vec<(&String, &InstructionPreset)> {
397        match preset_type {
398            Some(typ) => self
399                .presets
400                .iter()
401                .filter(|(_, preset)| preset.preset_type == typ)
402                .collect(),
403            None => self.list_presets(),
404        }
405    }
406
407    #[must_use]
408    pub fn list_valid_presets_for_command(
409        &self,
410        command_type: PresetType,
411    ) -> Vec<(&String, &InstructionPreset)> {
412        self.presets
413            .iter()
414            .filter(|(_, preset)| {
415                preset.preset_type == command_type || preset.preset_type == PresetType::Both
416            })
417            .collect()
418    }
419}
420
421#[must_use]
422pub fn get_instruction_preset_library() -> InstructionPresetLibrary {
423    InstructionPresetLibrary::new()
424}
425
426#[must_use]
427pub fn list_presets_formatted(library: &InstructionPresetLibrary) -> String {
428    list_presets_formatted_by_type(library, None)
429}
430
431#[must_use]
432pub fn list_presets_formatted_by_type(
433    library: &InstructionPresetLibrary,
434    preset_type: Option<PresetType>,
435) -> String {
436    let mut presets: Vec<_> = library.list_presets_by_type(preset_type);
437    presets.sort_by(|a, b| {
438        if a.1.name == "Default" {
439            std::cmp::Ordering::Less
440        } else if b.1.name == "Default" {
441            std::cmp::Ordering::Greater
442        } else {
443            a.1.name.cmp(&b.1.name)
444        }
445    });
446
447    presets
448        .iter()
449        .map(|(key, preset)| {
450            let type_indicator = match preset.preset_type {
451                PresetType::Commit => "[C]",
452                PresetType::Review => "[R]",
453                PresetType::Both => "[B]",
454            };
455            format!(
456                "{} {} - {} - {} - {}",
457                type_indicator, key, preset.emoji, preset.name, preset.description
458            )
459        })
460        .collect::<Vec<String>>()
461        .join("\n")
462}
463
464#[must_use]
465pub fn list_valid_presets_for_command_formatted(
466    library: &InstructionPresetLibrary,
467    command_type: PresetType,
468) -> String {
469    let mut presets: Vec<_> = library.list_valid_presets_for_command(command_type);
470    presets.sort_by(|a, b| {
471        if a.1.name == "Default" {
472            std::cmp::Ordering::Less
473        } else if b.1.name == "Default" {
474            std::cmp::Ordering::Greater
475        } else {
476            a.1.name.cmp(&b.1.name)
477        }
478    });
479
480    presets
481        .iter()
482        .map(|(key, preset)| {
483            let type_indicator = match preset.preset_type {
484                PresetType::Commit => "[C]",
485                PresetType::Review => "[R]",
486                PresetType::Both => "[B]",
487            };
488            format!(
489                "{} {} - {} - {} - {}",
490                type_indicator, key, preset.emoji, preset.name, preset.description
491            )
492        })
493        .collect::<Vec<String>>()
494        .join("\n")
495}