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