1use std::path::PathBuf;
2
3use crate::AgentServerCommand;
4use anyhow::Result;
5use collections::HashMap;
6use gpui::{App, SharedString};
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
10
11pub fn init(cx: &mut App) {
12 AllAgentServersSettings::register(cx);
13}
14
15#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, SettingsUi, SettingsKey)]
16#[settings_key(key = "agent_servers")]
17pub struct AllAgentServersSettings {
18 pub gemini: Option<BuiltinAgentServerSettings>,
19 pub claude: Option<CustomAgentServerSettings>,
20
21 /// Custom agent servers configured by the user
22 #[serde(flatten)]
23 pub custom: HashMap<SharedString, CustomAgentServerSettings>,
24}
25
26#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)]
27pub struct BuiltinAgentServerSettings {
28 /// Absolute path to a binary to be used when launching this agent.
29 ///
30 /// This can be used to run a specific binary without automatic downloads or searching `$PATH`.
31 #[serde(rename = "command")]
32 pub path: Option<PathBuf>,
33 /// If a binary is specified in `command`, it will be passed these arguments.
34 pub args: Option<Vec<String>>,
35 /// If a binary is specified in `command`, it will be passed these environment variables.
36 pub env: Option<HashMap<String, String>>,
37 /// Whether to skip searching `$PATH` for an agent server binary when
38 /// launching this agent.
39 ///
40 /// This has no effect if a `command` is specified. Otherwise, when this is
41 /// `false`, Zed will search `$PATH` for an agent server binary and, if one
42 /// is found, use it for threads with this agent. If no agent binary is
43 /// found on `$PATH`, Zed will automatically install and use its own binary.
44 /// When this is `true`, Zed will not search `$PATH`, and will always use
45 /// its own binary.
46 ///
47 /// Default: true
48 pub ignore_system_version: Option<bool>,
49}
50
51impl BuiltinAgentServerSettings {
52 pub(crate) fn custom_command(self) -> Option<AgentServerCommand> {
53 self.path.map(|path| AgentServerCommand {
54 path,
55 args: self.args.unwrap_or_default(),
56 env: self.env,
57 })
58 }
59}
60
61impl From<AgentServerCommand> for BuiltinAgentServerSettings {
62 fn from(value: AgentServerCommand) -> Self {
63 BuiltinAgentServerSettings {
64 path: Some(value.path),
65 args: Some(value.args),
66 env: value.env,
67 ..Default::default()
68 }
69 }
70}
71
72#[derive(Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)]
73pub struct CustomAgentServerSettings {
74 #[serde(flatten)]
75 pub command: AgentServerCommand,
76}
77
78impl settings::Settings for AllAgentServersSettings {
79 type FileContent = Self;
80
81 fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
82 let mut settings = AllAgentServersSettings::default();
83
84 for AllAgentServersSettings {
85 gemini,
86 claude,
87 custom,
88 } in sources.defaults_and_customizations()
89 {
90 if gemini.is_some() {
91 settings.gemini = gemini.clone();
92 }
93 if claude.is_some() {
94 settings.claude = claude.clone();
95 }
96
97 // Merge custom agents
98 for (name, config) in custom {
99 // Skip built-in agent names to avoid conflicts
100 if name != "gemini" && name != "claude" {
101 settings.custom.insert(name.clone(), config.clone());
102 }
103 }
104 }
105
106 Ok(settings)
107 }
108
109 fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
110}