1use crate::config::Config;
2use crate::core::llm::get_available_provider_names;
3use anyhow::Result;
4use clap::Args;
5use std::fmt::Write;
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 presets' to see available presets for commits and reviews)"
52 )]
53 pub preset: Option<String>,
54
55 #[arg(long, help = "Enable or disable emoji")]
57 pub emoji: 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<bool> {
78 let mut changes_made = false;
79
80 if let Some(provider) = &self.provider {
81 let provider_name = if provider.to_lowercase() == "claude" {
83 "anthropic".to_string()
84 } else if provider.to_lowercase() == "openrouter" {
85 "openrouter".to_string()
86 } else {
87 provider.clone()
88 };
89
90 if config.default_provider != provider_name {
92 if !config.providers.contains_key(&provider_name) {
94 use crate::config::ProviderConfig;
96 config.providers.insert(
97 provider_name.clone(),
98 ProviderConfig::default_for(&provider_name),
99 );
100 }
101
102 config.default_provider.clone_from(&provider_name);
103 changes_made = true;
104 }
105 }
106
107 if let Some(instructions) = &self.instructions {
108 config.set_temp_instructions(Some(instructions.clone()));
109 }
111
112 Ok(changes_made)
113 }
114}
115
116pub fn available_providers_parser(s: &str) -> Result<String, String> {
118 let mut provider_name = s.to_lowercase();
119
120 if provider_name == "claude" {
122 provider_name = "anthropic".to_string();
123 }
124
125 let available_providers = get_available_provider_names();
126
127 if available_providers
128 .iter()
129 .any(|p| p.to_lowercase() == provider_name)
130 {
131 Ok(provider_name)
132 } else {
133 Err(format!(
134 "Invalid provider '{}'. Available providers: {}",
135 s,
136 available_providers.join(", ")
137 ))
138 }
139}
140
141pub fn get_combined_instructions(config: &Config) -> String {
142 let mut prompt = String::from("\n\n");
143
144 if !config.instructions.is_empty() {
145 write!(
146 &mut prompt,
147 "\n\nAdditional instructions for the request:\n{}\n\n",
148 config.instructions
149 )
150 .expect("write to string should not fail");
151 }
152
153 prompt
154}