use crate::Agent;
use std::{
io,
path::{Path, PathBuf},
};
#[derive(Debug, Clone, Default)]
pub struct Roots {
pub claude: Option<PathBuf>,
pub codex: Option<PathBuf>,
}
impl Roots {
pub fn from_home(home: &Path) -> Self {
Self {
claude: Some(home.join(".claude")),
codex: Some(home.join(".codex")),
}
}
pub fn from_env() -> io::Result<Self> {
Ok(Self {
claude: Self::from_env_for(Agent::ClaudeCode)?.claude,
codex: Self::from_env_for(Agent::Codex)?.codex,
})
}
pub fn from_env_for(agent: Agent) -> io::Result<Self> {
let home = std::env::var_os("HOME")
.filter(|v| !v.is_empty())
.map(PathBuf::from)
.filter(|p| !cfg!(windows) || p.is_absolute())
.or_else(std::env::home_dir);
let (key, suffix) = match agent {
Agent::ClaudeCode => ("CLAUDE_CONFIG_DIR", ".claude"),
Agent::Codex => ("CODEX_HOME", ".codex"),
_ => {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
"use discover_directory with an explicit native session root",
));
}
};
let path = match std::env::var_os(key) {
Some(v) if v.is_empty() => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("{key} is empty"),
));
}
Some(v) => Some(PathBuf::from(v)),
None => home.map(|h| h.join(suffix)),
};
Ok(match agent {
Agent::ClaudeCode => Self {
claude: path,
codex: None,
},
Agent::Codex => Self {
claude: None,
codex: path,
},
_ => unreachable!("unsupported agents returned above"),
})
}
}