1use std::rc::Rc;
2use std::{any::Any, path::Path};
3
4use crate::{AgentServer, AgentServerDelegate};
5use acp_thread::AgentConnection;
6use anyhow::{Context as _, Result};
7use client::ProxySettings;
8use collections::HashMap;
9use gpui::{App, AppContext, SharedString, Task};
10use language_models::provider::google::GoogleLanguageModelProvider;
11use project::agent_server_store::GEMINI_NAME;
12use settings::SettingsStore;
13
14#[derive(Clone)]
15pub struct Gemini;
16
17impl AgentServer for Gemini {
18 fn telemetry_id(&self) -> &'static str {
19 "gemini-cli"
20 }
21
22 fn name(&self) -> SharedString {
23 "Gemini CLI".into()
24 }
25
26 fn logo(&self) -> ui::IconName {
27 ui::IconName::AiGemini
28 }
29
30 fn connect(
31 &self,
32 root_dir: Option<&Path>,
33 delegate: AgentServerDelegate,
34 cx: &mut App,
35 ) -> Task<Result<(Rc<dyn AgentConnection>, Option<task::SpawnInTerminal>)>> {
36 let name = self.name();
37 let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().to_string());
38 let is_remote = delegate.project.read(cx).is_via_remote_server();
39 let store = delegate.store.downgrade();
40 let proxy_url = cx.read_global(|settings: &SettingsStore, _| {
41 settings.get::<ProxySettings>(None).proxy.clone()
42 });
43
44 cx.spawn(async move |cx| {
45 let mut extra_env = HashMap::default();
46 if let Some(api_key) = cx.update(GoogleLanguageModelProvider::api_key)?.await.ok() {
47 extra_env.insert("GEMINI_API_KEY".into(), api_key.key);
48 }
49 let (mut command, root_dir, login) = store
50 .update(cx, |store, cx| {
51 let agent = store
52 .get_external_agent(&GEMINI_NAME.into())
53 .context("Gemini CLI is not registered")?;
54 anyhow::Ok(agent.get_command(
55 root_dir.as_deref(),
56 extra_env,
57 delegate.status_tx,
58 delegate.new_version_available,
59 &mut cx.to_async(),
60 ))
61 })??
62 .await?;
63
64 // Add proxy flag if proxy settings are configured in Zed and not in the args
65 if let Some(proxy_url_value) = &proxy_url
66 && !command.args.iter().any(|arg| arg.contains("--proxy"))
67 {
68 command.args.push("--proxy".into());
69 command.args.push(proxy_url_value.clone());
70 }
71
72 let connection =
73 crate::acp::connect(name, command, root_dir.as_ref(), is_remote, cx).await?;
74 Ok((connection, login))
75 })
76 }
77
78 fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
79 self
80 }
81}
82
83#[cfg(test)]
84pub(crate) mod tests {
85 use project::agent_server_store::AgentServerCommand;
86
87 use super::*;
88 use std::path::Path;
89
90 crate::common_e2e_tests!(async |_, _, _| Gemini, allow_option_id = "proceed_once");
91
92 pub fn local_command() -> AgentServerCommand {
93 let cli_path = Path::new(env!("CARGO_MANIFEST_DIR"))
94 .join("../../../gemini-cli/packages/cli")
95 .to_string_lossy()
96 .to_string();
97
98 AgentServerCommand {
99 path: "node".into(),
100 args: vec![cli_path],
101 env: None,
102 }
103 }
104}