claude.rs

  1use language_models::provider::anthropic::AnthropicLanguageModelProvider;
  2use settings::SettingsStore;
  3use std::path::Path;
  4use std::rc::Rc;
  5use std::{any::Any, path::PathBuf};
  6
  7use anyhow::Result;
  8use gpui::{App, AppContext as _, SharedString, Task};
  9
 10use crate::{AgentServer, AgentServerDelegate, AllAgentServersSettings};
 11use acp_thread::AgentConnection;
 12
 13#[derive(Clone)]
 14pub struct ClaudeCode;
 15
 16pub struct ClaudeCodeLoginCommand {
 17    pub path: PathBuf,
 18    pub arguments: Vec<String>,
 19}
 20
 21impl ClaudeCode {
 22    const BINARY_NAME: &'static str = "claude-code-acp";
 23    const PACKAGE_NAME: &'static str = "@zed-industries/claude-code-acp";
 24
 25    pub fn login_command(
 26        delegate: AgentServerDelegate,
 27        cx: &mut App,
 28    ) -> Task<Result<ClaudeCodeLoginCommand>> {
 29        let settings = cx.read_global(|settings: &SettingsStore, _| {
 30            settings.get::<AllAgentServersSettings>(None).claude.clone()
 31        });
 32
 33        cx.spawn(async move |cx| {
 34            let mut command = if let Some(settings) = settings {
 35                settings.command
 36            } else {
 37                cx.update(|cx| {
 38                    delegate.get_or_npm_install_builtin_agent(
 39                        Self::BINARY_NAME.into(),
 40                        Self::PACKAGE_NAME.into(),
 41                        "node_modules/@anthropic-ai/claude-code/cli.js".into(),
 42                        true,
 43                        None,
 44                        cx,
 45                    )
 46                })?
 47                .await?
 48            };
 49            command.args.push("/login".into());
 50
 51            Ok(ClaudeCodeLoginCommand {
 52                path: command.path,
 53                arguments: command.args,
 54            })
 55        })
 56    }
 57}
 58
 59impl AgentServer for ClaudeCode {
 60    fn telemetry_id(&self) -> &'static str {
 61        "claude-code"
 62    }
 63
 64    fn name(&self) -> SharedString {
 65        "Claude Code".into()
 66    }
 67
 68    fn logo(&self) -> ui::IconName {
 69        ui::IconName::AiClaude
 70    }
 71
 72    fn connect(
 73        &self,
 74        root_dir: &Path,
 75        delegate: AgentServerDelegate,
 76        cx: &mut App,
 77    ) -> Task<Result<Rc<dyn AgentConnection>>> {
 78        let root_dir = root_dir.to_path_buf();
 79        let server_name = self.name();
 80        let settings = cx.read_global(|settings: &SettingsStore, _| {
 81            settings.get::<AllAgentServersSettings>(None).claude.clone()
 82        });
 83
 84        cx.spawn(async move |cx| {
 85            let mut command = if let Some(settings) = settings {
 86                settings.command
 87            } else {
 88                cx.update(|cx| {
 89                    delegate.get_or_npm_install_builtin_agent(
 90                        Self::BINARY_NAME.into(),
 91                        Self::PACKAGE_NAME.into(),
 92                        format!("node_modules/{}/dist/index.js", Self::PACKAGE_NAME).into(),
 93                        true,
 94                        None,
 95                        cx,
 96                    )
 97                })?
 98                .await?
 99            };
100
101            if let Some(api_key) = cx
102                .update(AnthropicLanguageModelProvider::api_key)?
103                .await
104                .ok()
105            {
106                command
107                    .env
108                    .get_or_insert_default()
109                    .insert("ANTHROPIC_API_KEY".to_owned(), api_key.key);
110            }
111
112            crate::acp::connect(server_name, command.clone(), &root_dir, cx).await
113        })
114    }
115
116    fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
117        self
118    }
119}