1use crate::stdio_agent_server::StdioAgentServer;
2use crate::{AgentServerCommand, AgentServerVersion};
3use anyhow::{Context as _, Result};
4use gpui::{AsyncApp, Entity};
5use project::Project;
6use settings::SettingsStore;
7
8use crate::AllAgentServersSettings;
9
10#[derive(Clone)]
11pub struct Gemini;
12
13const ACP_ARG: &str = "--experimental-acp";
14
15impl StdioAgentServer for Gemini {
16 fn name(&self) -> &'static str {
17 "Gemini"
18 }
19
20 fn empty_state_headline(&self) -> &'static str {
21 "Welcome to Gemini"
22 }
23
24 fn empty_state_message(&self) -> &'static str {
25 "Ask questions, edit files, run commands.\nBe specific for the best results."
26 }
27
28 fn logo(&self) -> ui::IconName {
29 ui::IconName::AiGemini
30 }
31
32 async fn command(
33 &self,
34 project: &Entity<Project>,
35 cx: &mut AsyncApp,
36 ) -> Result<AgentServerCommand> {
37 let settings = cx.read_global(|settings: &SettingsStore, _| {
38 settings.get::<AllAgentServersSettings>(None).gemini.clone()
39 })?;
40
41 if let Some(command) =
42 AgentServerCommand::resolve("gemini", &[ACP_ARG], settings, &project, cx).await
43 {
44 return Ok(command);
45 };
46
47 let (fs, node_runtime) = project.update(cx, |project, _| {
48 (project.fs().clone(), project.node_runtime().cloned())
49 })?;
50 let node_runtime = node_runtime.context("gemini not found on path")?;
51
52 let directory = ::paths::agent_servers_dir().join("gemini");
53 fs.create_dir(&directory).await?;
54 node_runtime
55 .npm_install_packages(&directory, &[("@google/gemini-cli", "latest")])
56 .await?;
57 let path = directory.join("node_modules/.bin/gemini");
58
59 Ok(AgentServerCommand {
60 path,
61 args: vec![ACP_ARG.into()],
62 env: None,
63 })
64 }
65
66 async fn version(&self, command: &AgentServerCommand) -> Result<AgentServerVersion> {
67 let version_fut = util::command::new_smol_command(&command.path)
68 .args(command.args.iter())
69 .arg("--version")
70 .kill_on_drop(true)
71 .output();
72
73 let help_fut = util::command::new_smol_command(&command.path)
74 .args(command.args.iter())
75 .arg("--help")
76 .kill_on_drop(true)
77 .output();
78
79 let (version_output, help_output) = futures::future::join(version_fut, help_fut).await;
80
81 let current_version = String::from_utf8(version_output?.stdout)?;
82 let supported = String::from_utf8(help_output?.stdout)?.contains(ACP_ARG);
83
84 if supported {
85 Ok(AgentServerVersion::Supported)
86 } else {
87 Ok(AgentServerVersion::Unsupported {
88 error_message: format!(
89 "Your installed version of Gemini {} doesn't support the Agentic Coding Protocol (ACP).",
90 current_version
91 ).into(),
92 upgrade_message: "Upgrade Gemini to Latest".into(),
93 upgrade_command: "npm install -g @google/gemini-cli@latest".into(),
94 })
95 }
96 }
97}
98
99#[cfg(test)]
100pub(crate) mod tests {
101 use super::*;
102 use crate::AgentServerCommand;
103 use std::path::Path;
104
105 crate::common_e2e_tests!(Gemini);
106
107 pub fn local_command() -> AgentServerCommand {
108 let cli_path = Path::new(env!("CARGO_MANIFEST_DIR"))
109 .join("../../../gemini-cli/packages/cli")
110 .to_string_lossy()
111 .to_string();
112
113 AgentServerCommand {
114 path: "node".into(),
115 args: vec![cli_path, ACP_ARG.into()],
116 env: None,
117 }
118 }
119}