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, pub preset_type: PresetType, }
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 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}