Skip to main content

recall_echo/
paths.rs

1//! Path resolution utilities for recall-echo.
2//!
3//! Supports two modes:
4//! 1. **Entity mode** (pulse-null) — entity_root/memory/ layout
5//! 2. **Claude mode** (standalone) — ~/.claude/ layout for Claude Code hooks
6
7use std::path::PathBuf;
8
9/// Returns the default entity root directory.
10///
11/// Resolution order:
12/// 1. RECALL_ECHO_HOME env var (explicit override)
13/// 2. Current working directory (for pulse-null entities)
14pub fn entity_root() -> Result<PathBuf, String> {
15    if let Ok(p) = std::env::var("RECALL_ECHO_HOME") {
16        return Ok(PathBuf::from(p));
17    }
18    std::env::current_dir().map_err(|e| format!("Could not determine working directory: {e}"))
19}
20
21/// Returns the memory directory: {entity_root}/memory/
22pub fn memory_dir() -> Result<PathBuf, String> {
23    Ok(entity_root()?.join("memory"))
24}
25
26pub fn memory_file() -> Result<PathBuf, String> {
27    Ok(memory_dir()?.join("MEMORY.md"))
28}
29
30pub fn ephemeral_file() -> Result<PathBuf, String> {
31    Ok(memory_dir()?.join("EPHEMERAL.md"))
32}
33
34pub fn archive_index() -> Result<PathBuf, String> {
35    Ok(memory_dir()?.join("ARCHIVE.md"))
36}
37
38pub fn conversations_dir() -> Result<PathBuf, String> {
39    Ok(memory_dir()?.join("conversations"))
40}
41
42pub fn config_file() -> Result<PathBuf, String> {
43    Ok(memory_dir()?.join(".recall-echo.toml"))
44}
45
46/// Returns the Claude Code base directory (~/.claude/).
47///
48/// Used when recall-echo is invoked as a Claude Code hook (archive-session,
49/// checkpoint). The memory layout inside ~/.claude/ mirrors the entity layout:
50/// ~/.claude/conversations/, ~/.claude/ARCHIVE.md, ~/.claude/EPHEMERAL.md, etc.
51pub fn claude_dir() -> Result<PathBuf, String> {
52    let home = dirs::home_dir().ok_or("Could not determine home directory")?;
53    Ok(home.join(".claude"))
54}
55
56/// Detect Claude Code installation.
57/// Returns Some(~/.claude/) if it exists, None otherwise.
58pub fn detect_claude_code() -> Option<PathBuf> {
59    let home = dirs::home_dir()?;
60    let claude = home.join(".claude");
61    if claude.exists() {
62        Some(claude)
63    } else {
64        None
65    }
66}