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 supports_always_allow(&self) -> bool {
29 true
30 }
31
32 fn logo(&self) -> ui::IconName {
33 ui::IconName::AiGemini
34 }
35
36 async fn command(
37 &self,
38 project: &Entity<Project>,
39 cx: &mut AsyncApp,
40 ) -> Result<AgentServerCommand> {
41 let settings = cx.read_global(|settings: &SettingsStore, _| {
42 settings.get::<AllAgentServersSettings>(None).gemini.clone()
43 })?;
44
45 if let Some(command) =
46 AgentServerCommand::resolve("gemini", &[ACP_ARG], settings, &project, cx).await
47 {
48 return Ok(command);
49 };
50
51 let (fs, node_runtime) = project.update(cx, |project, _| {
52 (project.fs().clone(), project.node_runtime().cloned())
53 })?;
54 let node_runtime = node_runtime.context("gemini not found on path")?;
55
56 let directory = ::paths::agent_servers_dir().join("gemini");
57 fs.create_dir(&directory).await?;
58 node_runtime
59 .npm_install_packages(&directory, &[("@google/gemini-cli", "latest")])
60 .await?;
61 let path = directory.join("node_modules/.bin/gemini");
62
63 Ok(AgentServerCommand {
64 path,
65 args: vec![ACP_ARG.into()],
66 env: None,
67 })
68 }
69
70 async fn version(&self, command: &AgentServerCommand) -> Result<AgentServerVersion> {
71 let version_fut = util::command::new_smol_command(&command.path)
72 .args(command.args.iter())
73 .arg("--version")
74 .kill_on_drop(true)
75 .output();
76
77 let help_fut = util::command::new_smol_command(&command.path)
78 .args(command.args.iter())
79 .arg("--help")
80 .kill_on_drop(true)
81 .output();
82
83 let (version_output, help_output) = futures::future::join(version_fut, help_fut).await;
84
85 let current_version = String::from_utf8(version_output?.stdout)?;
86 let supported = String::from_utf8(help_output?.stdout)?.contains(ACP_ARG);
87
88 if supported {
89 Ok(AgentServerVersion::Supported)
90 } else {
91 Ok(AgentServerVersion::Unsupported {
92 error_message: format!(
93 "Your installed version of Gemini {} doesn't support the Agentic Coding Protocol (ACP).",
94 current_version
95 ).into(),
96 upgrade_message: "Upgrade Gemini to Latest".into(),
97 upgrade_command: "npm install -g @google/gemini-cli@latest".into(),
98 })
99 }
100 }
101}
102
103#[cfg(test)]
104pub(crate) mod tests {
105 use super::*;
106 use crate::AgentServerCommand;
107 use std::path::Path;
108
109 crate::common_e2e_tests!(Gemini);
110
111 pub fn local_command() -> AgentServerCommand {
112 let cli_path = Path::new(env!("CARGO_MANIFEST_DIR"))
113 .join("../../../gemini-cli/packages/cli")
114 .to_string_lossy()
115 .to_string();
116
117 AgentServerCommand {
118 path: "node".into(),
119 args: vec![cli_path, ACP_ARG.into()],
120 env: None,
121 }
122 }
123}