use super::super::types::ProviderEntry;
#[allow(dead_code)]
pub enum ModelFlagPreset {
Flag(&'static str),
Env(&'static str),
Skip,
}
pub struct CliPreset {
pub name: &'static str,
pub label: &'static str,
pub binary: &'static str,
pub default_args: &'static [&'static str],
pub default_model: &'static str,
pub yolo_args: &'static [&'static str],
pub model_flag: ModelFlagPreset,
pub yolo_env: &'static [(&'static str, &'static str)],
pub max_turns_flag: Option<&'static str>,
}
pub const CLI_PRESETS: &[CliPreset] = &[
CliPreset {
name: "claude",
label: "Claude Code",
binary: "claude",
default_args: &["-p", "--output-format", "stream-json"],
default_model: "sonnet",
yolo_args: &["--dangerously-skip-permissions"],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: Some("--max-turns"),
},
CliPreset {
name: "codex",
label: "Codex CLI",
binary: "codex",
default_args: &["exec", "--json"],
default_model: "codex-mini-latest",
yolo_args: &["--full-auto"],
model_flag: ModelFlagPreset::Flag("-m"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "gemini",
label: "Gemini CLI",
binary: "gemini",
default_args: &["-p", "--output-format", "json"],
default_model: "gemini-2.5-pro",
yolo_args: &["--yolo"],
model_flag: ModelFlagPreset::Flag("-m"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "qwen",
label: "Qwen Code",
binary: "qwen-code",
default_args: &["-p"],
default_model: "qwen3-coder",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "cline",
label: "Cline CLI",
binary: "cline",
default_args: &["-y", "--json"],
default_model: "sonnet",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("-m"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "copilot",
label: "Copilot CLI",
binary: "copilot-cli",
default_args: &["-p"],
default_model: "gpt-4.1",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "cursor",
label: "Cursor CLI",
binary: "agent",
default_args: &["-p"],
default_model: "composer-2",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "goose",
label: "Goose",
binary: "goose",
default_args: &["run", "-t"],
default_model: "sonnet",
yolo_args: &[],
model_flag: ModelFlagPreset::Env("GOOSE_MODEL"),
yolo_env: &[("GOOSE_MODE", "auto")],
max_turns_flag: None,
},
CliPreset {
name: "continue",
label: "Continue CLI",
binary: "cn",
default_args: &["-p"],
default_model: "sonnet",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "aider",
label: "Aider",
binary: "aider",
default_args: &["--yes", "--no-auto-commits", "--message"],
default_model: "sonnet",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "junie",
label: "Junie CLI",
binary: "junie",
default_args: &["--output-format", "json"],
default_model: "junie-agent-2",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "vibe",
label: "Mistral Vibe",
binary: "mistral-vibe",
default_args: &["-p"],
default_model: "devstral-small-latest",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: Some("--max-turns"),
},
CliPreset {
name: "amp",
label: "Amp CLI",
binary: "amp",
default_args: &["exec"],
default_model: "sonnet",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "opencode",
label: "OpenCode",
binary: "opencode",
default_args: &[],
default_model: "sonnet",
yolo_args: &[],
model_flag: ModelFlagPreset::Flag("--model"),
yolo_env: &[],
max_turns_flag: None,
},
CliPreset {
name: "q",
label: "Amazon Q",
binary: "q",
default_args: &["chat", "--no-interactive"],
default_model: "amazon-q-developer",
yolo_args: &["--trust-all-tools"],
model_flag: ModelFlagPreset::Skip,
yolo_env: &[],
max_turns_flag: None,
},
];
pub fn find_cli_preset(query: &str) -> Option<&'static CliPreset> {
let q = query.to_lowercase();
CLI_PRESETS
.iter()
.find(|p| p.name == q || p.binary == q || p.label.to_lowercase().contains(&q))
}
pub fn scan_cli_presets() -> Vec<&'static CliPreset> {
CLI_PRESETS
.iter()
.filter(|p| {
std::process::Command::new("which")
.arg(p.binary)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
})
.collect()
}
pub fn cli_preset_to_provider(preset: &CliPreset) -> ProviderEntry {
ProviderEntry {
name: preset.name.to_string(),
base_url: String::new(),
api_key_enc: None,
models: if preset.default_model.is_empty() {
vec![]
} else {
vec![preset.default_model.to_string()]
},
cli: Some(preset.binary.to_string()),
cli_args: preset.default_args.iter().map(|s| s.to_string()).collect(),
cli_yolo_args: preset.yolo_args.iter().map(|s| s.to_string()).collect(),
cli_model_env: match preset.model_flag {
ModelFlagPreset::Env(var) => Some(var.to_string()),
_ => None,
},
cli_skip_model: matches!(preset.model_flag, ModelFlagPreset::Skip),
cli_yolo_env: preset
.yolo_env
.iter()
.map(|(k, v)| format!("{k}={v}"))
.collect(),
cli_max_turns_flag: preset.max_turns_flag.map(|s| s.to_string()),
}
}
pub struct ProviderPreset {
pub label: &'static str,
pub base_url: &'static str,
pub default_model: &'static str,
pub env_key_hint: &'static str,
pub recommended_models: &'static [&'static str],
pub default_supports_tools: bool,
pub api_key_required: bool,
pub architect_model: &'static str,
pub code_model: &'static str,
pub ask_model: &'static str,
}
pub const PROVIDERS: &[ProviderPreset] = &[
ProviderPreset {
label: "Anthropic",
base_url: "https://api.anthropic.com/v1",
default_model: "sonnet",
env_key_hint: "ANTHROPIC_API_KEY",
recommended_models: &["sonnet", "opus", "haiku"],
default_supports_tools: true,
api_key_required: true,
architect_model: "opus",
code_model: "sonnet",
ask_model: "haiku",
},
ProviderPreset {
label: "OpenAI",
base_url: "https://api.openai.com/v1",
default_model: "gpt-4o",
env_key_hint: "OPENAI_API_KEY",
recommended_models: &["gpt-4o", "gpt-4o-mini", "o3-mini", "gpt-4-turbo"],
default_supports_tools: true,
api_key_required: true,
architect_model: "o3-mini",
code_model: "gpt-4o",
ask_model: "gpt-4o-mini",
},
ProviderPreset {
label: "Google Gemini",
base_url: "https://generativelanguage.googleapis.com/v1beta/openai",
default_model: "gemini-2.5-flash",
env_key_hint: "GEMINI_API_KEY",
recommended_models: &["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.0-flash"],
default_supports_tools: true,
api_key_required: true,
architect_model: "gemini-2.5-pro",
code_model: "gemini-2.5-flash",
ask_model: "gemini-2.0-flash",
},
ProviderPreset {
label: "DeepSeek",
base_url: "https://api.deepseek.com/v1",
default_model: "deepseek-chat",
env_key_hint: "DEEPSEEK_API_KEY",
recommended_models: &["deepseek-chat", "deepseek-coder", "deepseek-reasoner"],
default_supports_tools: true,
api_key_required: true,
architect_model: "deepseek-reasoner",
code_model: "deepseek-coder",
ask_model: "deepseek-chat",
},
ProviderPreset {
label: "OpenRouter",
base_url: "https://openrouter.ai/api/v1",
default_model: "anthropic/claude-sonnet-4-6",
env_key_hint: "OPENROUTER_API_KEY",
recommended_models: &[
"anthropic/claude-sonnet-4-6",
"openai/gpt-4o",
"google/gemini-2.5-pro",
],
default_supports_tools: true,
api_key_required: true,
architect_model: "anthropic/claude-opus-4-6",
code_model: "anthropic/claude-sonnet-4-6",
ask_model: "anthropic/claude-haiku-4-5",
},
ProviderPreset {
label: "Groq",
base_url: "https://api.groq.com/openai/v1",
default_model: "llama-3.3-70b-versatile",
env_key_hint: "GROQ_API_KEY",
recommended_models: &["llama-3.3-70b-versatile", "llama3-70b-8192"],
default_supports_tools: true,
api_key_required: true,
architect_model: "llama-3.3-70b-versatile",
code_model: "llama-3.3-70b-versatile",
ask_model: "llama3-70b-8192",
},
ProviderPreset {
label: "Mistral",
base_url: "https://api.mistral.ai/v1",
default_model: "mistral-large-latest",
env_key_hint: "MISTRAL_API_KEY",
recommended_models: &[
"mistral-large-latest",
"codestral-latest",
"devstral-latest",
],
default_supports_tools: true,
api_key_required: true,
architect_model: "mistral-large-latest",
code_model: "devstral-latest",
ask_model: "codestral-latest",
},
ProviderPreset {
label: "Together AI",
base_url: "https://api.together.xyz/v1",
default_model: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
env_key_hint: "TOGETHER_API_KEY",
recommended_models: &[
"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
],
default_supports_tools: true,
api_key_required: true,
architect_model: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
code_model: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
ask_model: "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
},
ProviderPreset {
label: "Ollama",
base_url: "http://localhost:11434/v1",
default_model: "qwen2.5-coder",
env_key_hint: "",
recommended_models: &["qwen2.5-coder", "llama3.2", "deepseek-coder-v2"],
default_supports_tools: false,
api_key_required: false,
architect_model: "deepseek-coder-v2",
code_model: "qwen2.5-coder",
ask_model: "llama3.2",
},
ProviderPreset {
label: "LM Studio",
base_url: "http://localhost:1234/v1",
default_model: "qwen2.5-7b-instruct",
env_key_hint: "",
recommended_models: &[
"qwen2.5-7b-instruct",
"llama-3.1-8b-instruct",
"deepseek-coder-v2",
"phi-3-mini",
],
default_supports_tools: true,
api_key_required: false,
architect_model: "deepseek-coder-v2",
code_model: "qwen2.5-7b-instruct",
ask_model: "phi-3-mini",
},
ProviderPreset {
label: "Azure OpenAI",
base_url: "",
default_model: "gpt-4o",
env_key_hint: "AZURE_OPENAI_API_KEY",
recommended_models: &["gpt-4o", "gpt-4o-mini"],
default_supports_tools: true,
api_key_required: true,
architect_model: "gpt-4o",
code_model: "gpt-4o",
ask_model: "gpt-4o-mini",
},
ProviderPreset {
label: "Amazon Bedrock",
base_url: "",
default_model: "anthropic.claude-sonnet-4-6-v1",
env_key_hint: "AWS_ACCESS_KEY_ID",
recommended_models: &[
"anthropic.claude-sonnet-4-6-v1",
"anthropic.claude-haiku-4-5-v1",
],
default_supports_tools: true,
api_key_required: true,
architect_model: "anthropic.claude-sonnet-4-6-v1",
code_model: "anthropic.claude-sonnet-4-6-v1",
ask_model: "anthropic.claude-haiku-4-5-v1",
},
ProviderPreset {
label: "Z.ai Coding (zai-coding)",
base_url: "https://api.z.ai/api/coding/paas/v4",
default_model: "glm-4.7",
env_key_hint: "ZAI_API_KEY",
recommended_models: &[
"glm-5",
"glm-4.7",
"glm-4.7-flash",
"glm-4.6",
"glm-4.5-air",
],
default_supports_tools: true,
api_key_required: true,
architect_model: "glm-5",
code_model: "glm-4.7",
ask_model: "glm-4.7-flash",
},
ProviderPreset {
label: "Custom",
base_url: "",
default_model: "",
env_key_hint: "",
recommended_models: &[],
default_supports_tools: true,
api_key_required: true,
architect_model: "",
code_model: "",
ask_model: "",
},
];
pub fn find_provider_preset(query: &str) -> Option<&'static ProviderPreset> {
let q = query.to_lowercase();
if let Some(p) = PROVIDERS.iter().find(|p| p.label.to_lowercase() == q) {
return Some(p);
}
PROVIDERS
.iter()
.find(|p| p.label.to_lowercase().contains(&q))
}
pub fn provider_short_name(label: &str) -> String {
if let Some(start) = label.rfind('(')
&& let Some(end) = label.rfind(')')
&& end > start + 1
{
return label[start + 1..end].to_string();
}
label.to_lowercase().replace(' ', "-")
}