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