1use agent_client_protocol as acp;
2use fs::Fs;
3use settings::{SettingsStore, update_settings_file};
4use std::path::Path;
5use std::rc::Rc;
6use std::sync::Arc;
7use std::{any::Any, path::PathBuf};
8
9use anyhow::{Context as _, Result};
10use collections::HashMap;
11use gpui::{App, AppContext as _, SharedString, Task};
12use project::agent_server_store::{AllAgentServersSettings, CLAUDE_CODE_NAME};
13
14use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
15use acp_thread::AgentConnection;
16
17#[derive(Clone)]
18pub struct ClaudeCode;
19
20pub struct AgentServerLoginCommand {
21 pub path: PathBuf,
22 pub arguments: Vec<String>,
23}
24
25impl AgentServer for ClaudeCode {
26 fn telemetry_id(&self) -> &'static str {
27 "claude-code"
28 }
29
30 fn name(&self) -> SharedString {
31 "Claude Code".into()
32 }
33
34 fn logo(&self) -> ui::IconName {
35 ui::IconName::AiClaude
36 }
37
38 fn default_mode(&self, cx: &mut App) -> Option<acp::SessionModeId> {
39 let settings = cx.read_global(|settings: &SettingsStore, _| {
40 settings.get::<AllAgentServersSettings>(None).claude.clone()
41 });
42
43 settings
44 .as_ref()
45 .and_then(|s| s.default_mode.clone().map(|m| acp::SessionModeId(m.into())))
46 }
47
48 fn set_default_mode(&self, mode_id: Option<acp::SessionModeId>, fs: Arc<dyn Fs>, cx: &mut App) {
49 update_settings_file(fs, cx, |settings, _| {
50 settings
51 .agent_servers
52 .get_or_insert_default()
53 .claude
54 .get_or_insert_default()
55 .default_mode = mode_id.map(|m| m.to_string())
56 });
57 }
58
59 fn connect(
60 &self,
61 root_dir: Option<&Path>,
62 delegate: AgentServerDelegate,
63 cx: &mut App,
64 ) -> Task<
65 Result<(
66 Rc<dyn AgentConnection>,
67 HashMap<String, task::SpawnInTerminal>,
68 )>,
69 > {
70 let name = self.name();
71 let root_dir = root_dir.map(|root_dir| root_dir.to_string_lossy().into_owned());
72 let is_remote = delegate.project.read(cx).is_via_remote_server();
73 let store = delegate.store.downgrade();
74 let extra_env = load_proxy_env(cx);
75 let default_mode = self.default_mode(cx);
76
77 cx.spawn(async move |cx| {
78 let (command, root_dir, auth_commands) = store
79 .update(cx, |store, cx| {
80 let agent = store
81 .get_external_agent(&CLAUDE_CODE_NAME.into())
82 .context("Claude Code is not registered")?;
83 anyhow::Ok(agent.get_command(
84 root_dir.as_deref(),
85 extra_env,
86 delegate.status_tx,
87 delegate.new_version_available,
88 &mut cx.to_async(),
89 ))
90 })??
91 .await?;
92 let connection = crate::acp::connect(
93 name,
94 command,
95 root_dir.as_ref(),
96 default_mode,
97 is_remote,
98 cx,
99 )
100 .await?;
101 Ok((connection, auth_commands))
102 })
103 }
104
105 fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
106 self
107 }
108}