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 #[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 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}