diff --git a/crates/project/src/agent_server_store.rs b/crates/project/src/agent_server_store.rs index bdb2297624e4a404cb3c918f07eab15004944f97..9c50f9060665ac459ec79c53028932961eb5a7fb 100644 --- a/crates/project/src/agent_server_store.rs +++ b/crates/project/src/agent_server_store.rs @@ -22,8 +22,8 @@ use rpc::{ }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use settings::{SettingsKey, SettingsSources, SettingsStore, SettingsUi}; -use util::{ResultExt as _, debug_panic}; +use settings::{SettingsContent, SettingsKey, SettingsStore, SettingsUi}; +use util::{MergeFrom, ResultExt as _, debug_panic}; use crate::ProjectEnvironment; @@ -994,47 +994,19 @@ impl ExternalAgentServer for LocalCustomAgent { pub const GEMINI_NAME: &'static str = "gemini"; pub const CLAUDE_CODE_NAME: &'static str = "claude"; -#[derive( - Default, Deserialize, Serialize, Clone, JsonSchema, Debug, SettingsUi, SettingsKey, PartialEq, -)] +#[derive(Default, Clone, JsonSchema, Debug, SettingsUi, SettingsKey, PartialEq)] #[settings_key(key = "agent_servers")] pub struct AllAgentServersSettings { pub gemini: Option, pub claude: Option, - - /// Custom agent servers configured by the user - #[serde(flatten)] pub custom: HashMap, } - -#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)] +#[derive(Default, Clone, JsonSchema, Debug, PartialEq)] pub struct BuiltinAgentServerSettings { - /// Absolute path to a binary to be used when launching this agent. - /// - /// This can be used to run a specific binary without automatic downloads or searching `$PATH`. - #[serde(rename = "command")] pub path: Option, - /// If a binary is specified in `command`, it will be passed these arguments. pub args: Option>, - /// If a binary is specified in `command`, it will be passed these environment variables. pub env: Option>, - /// Whether to skip searching `$PATH` for an agent server binary when - /// launching this agent. - /// - /// This has no effect if a `command` is specified. Otherwise, when this is - /// `false`, Zed will search `$PATH` for an agent server binary and, if one - /// is found, use it for threads with this agent. If no agent binary is - /// found on `$PATH`, Zed will automatically install and use its own binary. - /// When this is `true`, Zed will not search `$PATH`, and will always use - /// its own binary. - /// - /// Default: true pub ignore_system_version: Option, - /// The default mode to use for this agent. - /// - /// Note: Not only all agents support modes. - /// - /// Default: None pub default_mode: Option, } @@ -1048,6 +1020,18 @@ impl BuiltinAgentServerSettings { } } +impl From for BuiltinAgentServerSettings { + fn from(value: settings::BuiltinAgentServerSettings) -> Self { + BuiltinAgentServerSettings { + path: value.path, + args: value.args, + env: value.env, + ignore_system_version: value.ignore_system_version, + default_mode: value.default_mode, + } + } +} + impl From for BuiltinAgentServerSettings { fn from(value: AgentServerCommand) -> Self { BuiltinAgentServerSettings { @@ -1059,9 +1043,8 @@ impl From for BuiltinAgentServerSettings { } } -#[derive(Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)] +#[derive(Clone, JsonSchema, Debug, PartialEq)] pub struct CustomAgentServerSettings { - #[serde(flatten)] pub command: AgentServerCommand, /// The default mode to use for this agent. /// @@ -1071,36 +1054,47 @@ pub struct CustomAgentServerSettings { pub default_mode: Option, } -impl settings::Settings for AllAgentServersSettings { - type FileContent = Self; - - fn load(sources: SettingsSources, _: &mut App) -> Result { - let mut settings = AllAgentServersSettings::default(); - - for AllAgentServersSettings { - gemini, - claude, - custom, - } in sources.defaults_and_customizations() - { - if gemini.is_some() { - settings.gemini = gemini.clone(); - } - if claude.is_some() { - settings.claude = claude.clone(); - } +impl From for CustomAgentServerSettings { + fn from(value: settings::CustomAgentServerSettings) -> Self { + CustomAgentServerSettings { + command: AgentServerCommand { + path: value.path, + args: value.args, + env: value.env, + }, + default_mode: value.default_mode, + } + } +} - // Merge custom agents - for (name, config) in custom { - // Skip built-in agent names to avoid conflicts - if name != GEMINI_NAME && name != CLAUDE_CODE_NAME { - settings.custom.insert(name.clone(), config.clone()); - } - } +impl settings::Settings for AllAgentServersSettings { + fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self { + let agent_settings = content.agent_servers.clone().unwrap(); + Self { + gemini: agent_settings.gemini.map(Into::into), + claude: agent_settings.claude.map(Into::into), + custom: agent_settings + .custom + .into_iter() + .map(|(k, v)| (k.clone(), v.into())) + .collect(), } + } - Ok(settings) + fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) { + let Some(content) = &content.agent_servers else { + return; + }; + if let Some(gemini) = content.gemini.clone() { + self.gemini = Some(gemini.into()) + }; + if let Some(claude) = content.claude.clone() { + self.claude = Some(claude.into()); + } + for (name, config) in content.custom.clone() { + self.custom.insert(name, config.into()); + } } - fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {} + fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut SettingsContent) {} } diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index 9fab8bcbf1c0a05f01eb2429bfa358f9fb324c36..8acbf49361bd7669732e35faad84f16ec99a7431 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -26,6 +26,7 @@ pub struct SettingsContent { pub theme: ThemeSettingsContent, pub agent: Option, + pub agent_servers: Option, /// Configuration of audio in Zed. pub audio: Option, diff --git a/crates/settings/src/settings_content/agent.rs b/crates/settings/src/settings_content/agent.rs index 370b69b070db50136db4a59d832a7a1bb2ca55e5..c051491a6537808241cc2eea30c25fa0dfd40a91 100644 --- a/crates/settings/src/settings_content/agent.rs +++ b/crates/settings/src/settings_content/agent.rs @@ -1,8 +1,8 @@ -use collections::IndexMap; +use collections::{HashMap, IndexMap}; use gpui::SharedString; use schemars::{JsonSchema, json_schema}; use serde::{Deserialize, Serialize}; -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, path::PathBuf, sync::Arc}; #[derive(Clone, Serialize, Deserialize, JsonSchema, Debug, Default)] pub struct AgentSettingsContent { @@ -262,3 +262,59 @@ impl From<&str> for LanguageModelProviderSetting { Self(provider.to_string()) } } + +#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)] +pub struct AllAgentServersSettings { + pub gemini: Option, + pub claude: Option, + + /// Custom agent servers configured by the user + #[serde(flatten)] + pub custom: HashMap, +} + +#[derive(Default, Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)] +pub struct BuiltinAgentServerSettings { + /// Absolute path to a binary to be used when launching this agent. + /// + /// This can be used to run a specific binary without automatic downloads or searching `$PATH`. + #[serde(rename = "command")] + pub path: Option, + /// If a binary is specified in `command`, it will be passed these arguments. + pub args: Option>, + /// If a binary is specified in `command`, it will be passed these environment variables. + pub env: Option>, + /// Whether to skip searching `$PATH` for an agent server binary when + /// launching this agent. + /// + /// This has no effect if a `command` is specified. Otherwise, when this is + /// `false`, Zed will search `$PATH` for an agent server binary and, if one + /// is found, use it for threads with this agent. If no agent binary is + /// found on `$PATH`, Zed will automatically install and use its own binary. + /// When this is `true`, Zed will not search `$PATH`, and will always use + /// its own binary. + /// + /// Default: true + pub ignore_system_version: Option, + /// The default mode to use for this agent. + /// + /// Note: Not only all agents support modes. + /// + /// Default: None + pub default_mode: Option, +} + +#[derive(Deserialize, Serialize, Clone, JsonSchema, Debug, PartialEq)] +pub struct CustomAgentServerSettings { + #[serde(rename = "command")] + pub path: PathBuf, + #[serde(default)] + pub args: Vec, + pub env: Option>, + /// The default mode to use for this agent. + /// + /// Note: Not only all agents support modes. + /// + /// Default: None + pub default_mode: Option, +}