gemini.rs

  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}