use crate::model::{settings, state};
use toml_edit::DocumentMut;
const MOVED: [&str; 8] = [
"appearance",
"opaque",
"cursor_blink",
"text_size",
"hue",
"chroma",
"wide_pages",
"wrap_code",
];
fn renamed(key: &str) -> &str {
match key {
"appearance" => "mode",
key => key,
}
}
pub fn run() {
let Ok(settings_path) = settings::path() else {
return;
};
let settings_body = std::fs::read_to_string(&settings_path).unwrap_or_default();
let Ok(mut settings) = settings_body.parse::<DocumentMut>() else {
return;
};
let state_path = state::path();
let mut state = state_path
.as_ref()
.and_then(|path| std::fs::read_to_string(path).ok())
.and_then(|body| body.parse::<DocumentMut>().ok());
let carried = state
.as_mut()
.is_some_and(|state| carry_appearance(state, &mut settings));
let dropped = drop_default_agents(&mut settings);
if !carried && !dropped {
return;
}
if let Some(dir) = settings_path.parent()
&& std::fs::create_dir_all(dir).is_ok()
&& std::fs::write(&settings_path, settings.to_string()).is_ok()
&& let (true, Some(path), Some(state)) = (carried, state_path, state)
{
let _ = std::fs::write(path, state.to_string());
}
}
pub fn carry_appearance(state: &mut DocumentMut, settings: &mut DocumentMut) -> bool {
let mut moved = false;
for key in MOVED {
let Some(value) = state.get(key).and_then(|held| held.as_value()).cloned() else {
continue;
};
let table = settings["appearance"].or_insert(toml_edit::table());
let Some(table) = table.as_table_mut() else {
return false;
};
table.set_implicit(false);
if !table.contains_key(renamed(key)) {
table[renamed(key)] = toml_edit::value(value);
}
state.remove(key);
moved = true;
}
moved
}
const DEFAULTED: [(&str, &str); 2] = [
("claude", "@agentclientprotocol/claude-agent-acp"),
("codex", "@agentclientprotocol/codex-acp"),
];
pub fn drop_default_agents(settings: &mut DocumentMut) -> bool {
let Some(agents) = settings
.get_mut("agents")
.and_then(|item| item.as_array_of_tables_mut())
else {
return false;
};
let prefix = agents
.get(0)
.filter(|table| defaulted(table))
.map(|table| table.decor().prefix())
.filter(|prefix| {
prefix.is_some_and(|held| held.as_str().is_some_and(|text| !text.trim().is_empty()))
})
.and_then(|prefix| prefix.cloned());
let before = agents.len();
agents.retain(|table| !defaulted(table));
if agents.len() == before {
return false;
}
if let Some(prefix) = prefix {
match agents.get_mut(0) {
Some(first) => first.decor_mut().set_prefix(prefix),
None => settings.as_table_mut().decor_mut().set_prefix(prefix),
}
}
true
}
fn defaulted(table: &toml_edit::Table) -> bool {
if table.contains_key("id") || table.contains_key("env") {
return false;
}
let field = |key| table.get(key).and_then(|value| value.as_str());
if field("command") != Some("npx") {
return false;
}
let Some(args) = table.get("args").and_then(|args| args.as_array()) else {
return false;
};
let args: Vec<&str> = args.iter().filter_map(|arg| arg.as_str()).collect();
let [flag, spec] = args[..] else {
return false;
};
let package = cacp_agents::package_name(spec);
flag == "-y" && DEFAULTED.contains(&(field("name").unwrap_or_default(), package))
}