Skip to main content

agent_sessions/
roots.rs

1use crate::Agent;
2use std::{
3    io,
4    path::{Path, PathBuf},
5};
6
7#[derive(Debug, Clone, Default)]
8pub struct Roots {
9    pub claude: Option<PathBuf>,
10    pub codex: Option<PathBuf>,
11}
12impl Roots {
13    pub fn from_home(home: &Path) -> Self {
14        Self {
15            claude: Some(home.join(".claude")),
16            codex: Some(home.join(".codex")),
17        }
18    }
19    pub fn from_env() -> io::Result<Self> {
20        Ok(Self {
21            claude: Self::from_env_for(Agent::ClaudeCode)?.claude,
22            codex: Self::from_env_for(Agent::Codex)?.codex,
23        })
24    }
25    /// Resolve only the selected host so unrelated environment errors cannot
26    /// disable a source-specific consumer.
27    pub fn from_env_for(agent: Agent) -> io::Result<Self> {
28        let home = std::env::var_os("HOME")
29            .filter(|v| !v.is_empty())
30            .map(PathBuf::from)
31            .filter(|p| !cfg!(windows) || p.is_absolute())
32            .or_else(std::env::home_dir);
33        let (key, suffix) = match agent {
34            Agent::ClaudeCode => ("CLAUDE_CONFIG_DIR", ".claude"),
35            Agent::Codex => ("CODEX_HOME", ".codex"),
36            _ => {
37                return Err(io::Error::new(
38                    io::ErrorKind::Unsupported,
39                    "use discover_directory with an explicit native session root",
40                ));
41            }
42        };
43        let path = match std::env::var_os(key) {
44            Some(v) if v.is_empty() => {
45                return Err(io::Error::new(
46                    io::ErrorKind::InvalidInput,
47                    format!("{key} is empty"),
48                ));
49            }
50            Some(v) => Some(PathBuf::from(v)),
51            None => home.map(|h| h.join(suffix)),
52        };
53        Ok(match agent {
54            Agent::ClaudeCode => Self {
55                claude: path,
56                codex: None,
57            },
58            Agent::Codex => Self {
59                claude: None,
60                codex: path,
61            },
62            _ => unreachable!("unsupported agents returned above"),
63        })
64    }
65}