1use std::path::Path;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Result;
6use crate::paths;
7
8pub const DEFAULT_AGENT: &str = "open-agents";
9
10#[derive(Debug, Serialize, Deserialize)]
11#[serde(default)]
12pub struct TuffConfig {
13 #[serde(rename = "agents", alias = "targets")]
14 pub agents: Vec<String>,
15 #[serde(rename = "defaultAgent", alias = "default_agent")]
16 pub default_agent: String,
17}
18
19impl Default for TuffConfig {
20 fn default() -> Self {
21 Self {
22 agents: vec![],
23 default_agent: DEFAULT_AGENT.to_string(),
24 }
25 }
26}
27
28pub fn read_config(repo_root: &Path) -> Result<TuffConfig> {
29 read_config_at(&paths::project_config(repo_root))
30}
31
32pub fn read_global_config(home: &Path) -> Result<TuffConfig> {
33 read_config_at(&paths::user_config(home))
34}
35
36fn read_config_at(path: &Path) -> Result<TuffConfig> {
37 if path.exists() {
38 let config: TuffConfig = serde_json::from_str(&std::fs::read_to_string(path)?)?;
39 Ok(config)
40 } else {
41 let default = TuffConfig::default();
42 write_config_at(path, &default)?;
43 Ok(default)
44 }
45}
46
47pub fn write_config(repo_root: &Path, config: &TuffConfig) -> Result<()> {
48 write_config_at(&paths::project_config(repo_root), config)
49}
50
51pub fn write_global_config(home: &Path, config: &TuffConfig) -> Result<()> {
52 write_config_at(&paths::user_config(home), config)
53}
54
55fn write_config_at(path: &Path, config: &TuffConfig) -> Result<()> {
56 if let Some(parent) = path.parent() {
57 std::fs::create_dir_all(parent)?;
58 }
59 std::fs::write(path, serde_json::to_string_pretty(config)? + "\n")?;
60 Ok(())
61}