1use crate::config::Config;
2use crate::core::llm::get_available_provider_names;
3use crate::instruction_presets::{PresetType, get_instruction_preset_library};
4use anyhow::Result;
5use clap::Args;
6use std::fmt::Write;
7use std::str::FromStr;
8
9#[derive(Clone, Copy, PartialEq, Eq, Debug)]
10pub enum DetailLevel {
11 Minimal,
12 Standard,
13 Detailed,
14}
15
16impl FromStr for DetailLevel {
17 type Err = anyhow::Error;
18
19 fn from_str(s: &str) -> Result<Self, Self::Err> {
20 match s.to_lowercase().as_str() {
21 "minimal" => Ok(Self::Minimal),
22 "standard" => Ok(Self::Standard),
23 "detailed" => Ok(Self::Detailed),
24 _ => Err(anyhow::anyhow!("Invalid detail level: {s}")),
25 }
26 }
27}
28
29impl DetailLevel {
30 pub fn as_str(&self) -> &'static str {
31 match self {
32 Self::Minimal => "minimal",
33 Self::Standard => "standard",
34 Self::Detailed => "detailed",
35 }
36 }
37}
38
39#[derive(Args, Clone, Default, Debug)]
40pub struct CommonParams {
41 #[arg(long, help = "Override default LLM provider", value_parser = available_providers_parser)]
43 pub provider: Option<String>,
44
45 #[arg(short, long, help = "Custom instructions for this operation")]
47 pub instructions: Option<String>,
48
49 #[arg(
51 long,
52 help = "Select an instruction preset (use 'git presets' to see available presets for commits and reviews)"
53 )]
54 pub preset: Option<String>,
55
56 #[arg(long, help = "Enable or disable emoji")]
58 pub emoji: Option<bool>,
59
60 #[arg(
62 long,
63 help = "Set the detail level (minimal, standard, detailed)",
64 default_value = "standard"
65 )]
66 pub detail_level: String,
67
68 #[arg(
70 short = 'r',
71 long = "repo",
72 help = "Repository URL to use instead of local repository"
73 )]
74 pub repository_url: Option<String>,
75}
76
77impl CommonParams {
78 pub fn apply_to_config(&self, config: &mut Config) -> Result<bool> {
79 let mut changes_made = false;
80
81 if let Some(provider) = &self.provider {
82 let provider_name = if provider.to_lowercase() == "claude" {
84 "anthropic".to_string()
85 } else if provider.to_lowercase() == "openrouter" {
86 "openrouter".to_string()
87 } else {
88 provider.clone()
89 };
90
91 if config.default_provider != provider_name {
93 if !config.providers.contains_key(&provider_name) {
95 use crate::config::ProviderConfig;
97 config.providers.insert(
98 provider_name.clone(),
99 ProviderConfig::default_for(&provider_name),
100 );
101 }
102
103 config.default_provider.clone_from(&provider_name);
104 changes_made = true;
105 }
106 }
107
108 if let Some(instructions) = &self.instructions {
109 config.set_temp_instructions(Some(instructions.clone()));
110 }
112
113 if let Some(preset) = &self.preset {
114 config.set_temp_preset(Some(preset.clone()));
115 }
117
118 if let Some(use_emoji) = self.emoji
119 && config.use_emoji != use_emoji
120 {
121 config.use_emoji = use_emoji;
122 changes_made = true;
123 }
124
125 Ok(changes_made)
126 }
127
128 pub fn is_valid_preset_for_type(&self, preset_type: PresetType) -> bool {
130 if let Some(preset_name) = &self.preset {
131 let library = get_instruction_preset_library();
132 let valid_presets = library.list_valid_presets_for_command(preset_type);
133 return valid_presets.iter().any(|(key, _)| *key == preset_name);
134 }
135 true }
137}
138
139pub fn available_providers_parser(s: &str) -> Result<String, String> {
141 let mut provider_name = s.to_lowercase();
142
143 if provider_name == "claude" {
145 provider_name = "anthropic".to_string();
146 }
147
148 let available_providers = get_available_provider_names();
149
150 if available_providers
151 .iter()
152 .any(|p| p.to_lowercase() == provider_name)
153 {
154 Ok(provider_name)
155 } else {
156 Err(format!(
157 "Invalid provider '{}'. Available providers: {}",
158 s,
159 available_providers.join(", ")
160 ))
161 }
162}
163
164pub fn get_combined_instructions(config: &Config) -> String {
165 let mut prompt = String::from("\n\n");
166
167 if !config.instructions.is_empty() {
168 write!(
169 &mut prompt,
170 "\n\nAdditional instructions for the request:\n{}\n\n",
171 config.instructions
172 )
173 .expect("write to string should not fail");
174 }
175
176 let preset_library = get_instruction_preset_library();
177 if let Some(preset_instructions) = preset_library.get_preset(config.instruction_preset.as_str())
178 {
179 write!(
180 &mut prompt,
181 "\n\nIMPORTANT: Use this style for your output:\n{}\n\n",
182 preset_instructions.instructions
183 )
184 .expect("write to string should not fail");
185 }
186
187 prompt
188}