1use crate::config::Config;
2use crate::instruction_presets::{PresetType, get_instruction_preset_library};
3use crate::llm;
4use anyhow::Result;
5use clap::Args;
6use std::str::FromStr;
7
8#[derive(Clone, Copy, PartialEq, Eq, Debug)]
9pub enum DetailLevel {
10 Minimal,
11 Standard,
12 Detailed,
13}
14
15impl FromStr for DetailLevel {
16 type Err = anyhow::Error;
17
18 fn from_str(s: &str) -> Result<Self, Self::Err> {
19 match s.to_lowercase().as_str() {
20 "minimal" => Ok(Self::Minimal),
21 "standard" => Ok(Self::Standard),
22 "detailed" => Ok(Self::Detailed),
23 _ => Err(anyhow::anyhow!("Invalid detail level: {}", s)),
24 }
25 }
26}
27
28impl DetailLevel {
29 pub fn as_str(&self) -> &'static str {
30 match self {
31 Self::Minimal => "minimal",
32 Self::Standard => "standard",
33 Self::Detailed => "detailed",
34 }
35 }
36}
37
38#[derive(Args, Clone, Default, Debug)]
39pub struct CommonParams {
40 #[arg(long, help = "Override default LLM provider", value_parser = available_providers_parser)]
42 pub provider: Option<String>,
43
44 #[arg(short, long, help = "Custom instructions for this operation")]
46 pub instructions: Option<String>,
47
48 #[arg(
50 long,
51 help = "Select an instruction preset (use 'git-iris list-presets' to see available presets for commits and reviews)"
52 )]
53 pub preset: Option<String>,
54
55 #[arg(long, help = "Enable or disable Gitmoji")]
57 pub gitmoji: Option<bool>,
58
59 #[arg(
61 long,
62 help = "Set the detail level (minimal, standard, detailed)",
63 default_value = "standard"
64 )]
65 pub detail_level: String,
66
67 #[arg(
69 short = 'r',
70 long = "repo",
71 help = "Repository URL to use instead of local repository"
72 )]
73 pub repository_url: Option<String>,
74}
75
76impl CommonParams {
77 pub fn apply_to_config(&self, config: &mut Config) -> Result<()> {
78 if let Some(provider) = &self.provider {
79 let provider_name = if provider.to_lowercase() == "claude" {
81 "anthropic".to_string()
82 } else {
83 provider.clone()
84 };
85 config.default_provider.clone_from(&provider_name);
86 }
87 if let Some(instructions) = &self.instructions {
88 config.set_temp_instructions(Some(instructions.clone()));
89 }
90 if let Some(preset) = &self.preset {
91 config.set_temp_preset(Some(preset.clone()));
92 }
93 if let Some(use_gitmoji) = self.gitmoji {
94 config.use_gitmoji = use_gitmoji;
95 }
96 Ok(())
97 }
98
99 pub fn is_valid_preset_for_type(&self, preset_type: PresetType) -> bool {
101 if let Some(preset_name) = &self.preset {
102 let library = get_instruction_preset_library();
103 let valid_presets = library.list_valid_presets_for_command(preset_type);
104 return valid_presets.iter().any(|(key, _)| *key == preset_name);
105 }
106 true }
108}
109
110pub fn available_providers_parser(s: &str) -> Result<String, String> {
112 let mut provider_name = s.to_lowercase();
113
114 if provider_name == "claude" {
116 provider_name = "anthropic".to_string();
117 }
118
119 let available_providers = llm::get_available_provider_names();
120
121 if available_providers
122 .iter()
123 .any(|p| p.to_lowercase() == provider_name)
124 {
125 Ok(provider_name)
126 } else {
127 Err(format!(
128 "Invalid provider '{}'. Available providers: {}",
129 s,
130 available_providers.join(", ")
131 ))
132 }
133}
134
135pub fn get_combined_instructions(config: &Config) -> String {
136 let mut prompt = String::from("\n\n");
137
138 if !config.instructions.is_empty() {
139 prompt.push_str(&format!(
140 "\n\nAdditional instructions for the request:\n{}\n\n",
141 config.instructions
142 ));
143 }
144
145 let preset_library = get_instruction_preset_library();
146 if let Some(preset_instructions) = preset_library.get_preset(config.instruction_preset.as_str())
147 {
148 prompt.push_str(&format!(
149 "\n\nIMPORTANT: Use this style for your output:\n{}\n\n",
150 preset_instructions.instructions
151 ));
152 }
153
154 prompt
155}