Skip to main content

acp_cli/
config.rs

1use crate::agent::registry::AgentOverride;
2use crate::client::permissions::PermissionMode;
3use crate::session::scoping::find_git_root;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::path::Path;
7
8#[derive(Debug, Deserialize, Serialize, Default, Clone)]
9pub struct AcpCliConfig {
10    pub default_agent: Option<String>,
11    pub default_permissions: Option<PermissionMode>,
12    pub timeout: Option<u64>,
13    pub format: Option<String>,
14    pub agents: Option<HashMap<String, AgentOverride>>,
15}
16
17impl AcpCliConfig {
18    /// Load the global config from `~/.acp-cli/config.json`.
19    pub fn load() -> Self {
20        dirs::home_dir()
21            .map(|h| h.join(".acp-cli").join("config.json"))
22            .map(Self::load_from)
23            .unwrap_or_default()
24    }
25
26    /// Load a project-level config by walking from `cwd` up to the git root
27    /// and reading `.acp-cli.json` there. Returns `Default` if not found.
28    pub fn load_project(cwd: &Path) -> Self {
29        find_git_root(cwd)
30            .map(|root| root.join(".acp-cli.json"))
31            .map(Self::load_from)
32            .unwrap_or_default()
33    }
34
35    pub fn load_from(path: impl AsRef<Path>) -> Self {
36        std::fs::read_to_string(path.as_ref())
37            .ok()
38            .and_then(|s| serde_json::from_str(&s).ok())
39            .unwrap_or_default()
40    }
41
42    /// Merge project config on top of global. Project wins for non-None fields.
43    pub fn merge(self, project: AcpCliConfig) -> AcpCliConfig {
44        AcpCliConfig {
45            default_agent: project.default_agent.or(self.default_agent),
46            default_permissions: project.default_permissions.or(self.default_permissions),
47            timeout: project.timeout.or(self.timeout),
48            format: project.format.or(self.format),
49            agents: match (self.agents, project.agents) {
50                (Some(mut base), Some(proj)) => {
51                    base.extend(proj);
52                    Some(base)
53                }
54                (base, proj) => proj.or(base),
55            },
56        }
57    }
58}