settings.rs

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