1use std::path::PathBuf;
12
13use crate::error::RecallError;
14
15pub fn entity_root() -> Result<PathBuf, RecallError> {
21 if let Ok(p) = std::env::var("RECALL_ECHO_HOME") {
22 return Ok(PathBuf::from(p));
23 }
24 std::env::current_dir().map_err(RecallError::from)
25}
26
27pub fn memory_dir() -> Result<PathBuf, RecallError> {
29 Ok(entity_root()?.join("memory"))
30}
31
32pub fn memory_file() -> Result<PathBuf, RecallError> {
33 Ok(memory_dir()?.join("MEMORY.md"))
34}
35
36pub fn ephemeral_file() -> Result<PathBuf, RecallError> {
37 Ok(memory_dir()?.join("EPHEMERAL.md"))
38}
39
40pub fn archive_index() -> Result<PathBuf, RecallError> {
41 Ok(memory_dir()?.join("ARCHIVE.md"))
42}
43
44pub fn conversations_dir() -> Result<PathBuf, RecallError> {
45 Ok(memory_dir()?.join("conversations"))
46}
47
48pub fn config_file() -> Result<PathBuf, RecallError> {
49 Ok(memory_dir()?.join(".recall-echo.toml"))
50}
51
52pub fn claude_dir() -> Result<PathBuf, RecallError> {
58 let home = dirs::home_dir()
59 .ok_or_else(|| RecallError::Other("Could not determine home directory".into()))?;
60 Ok(home.join(".claude"))
61}
62
63#[must_use]
65pub fn expand_tilde(path: &str) -> String {
66 if let Some(rest) = path.strip_prefix("~/") {
67 if let Some(home) = dirs::home_dir() {
68 return home.join(rest).to_string_lossy().to_string();
69 }
70 }
71 path.to_string()
72}
73
74#[must_use]
77pub fn detect_claude_code() -> Option<PathBuf> {
78 if let Some(dir) = std::env::var_os(CLAUDE_DIR_ENV) {
83 let claude = PathBuf::from(dir);
84 return claude.exists().then_some(claude);
85 }
86 let home = dirs::home_dir()?;
87 let claude = home.join(".claude");
88 if claude.exists() {
89 Some(claude)
90 } else {
91 None
92 }
93}
94
95pub const CLAUDE_DIR_ENV: &str = "RECALL_ECHO_CLAUDE_DIR";