Skip to main content

forge_agent/userdata/
memory.rs

1//! Readers for the user's `CLAUDE.md` (per-repo instructions) and the
2//! per-project auto-memory file
3//! (`<config_dir>/projects/<project_key>/memory/MEMORY.md`).
4//!
5//! The caller (typically a `ForgeSdkBridge`) threads the resolved
6//! `config_dir` in; the project-key sanitiser from
7//! `forge_agent::userdata::catalog::scan` keeps the on-disk layout
8//! aligned with what the `claude` CLI writes.
9
10use std::path::{Path, PathBuf};
11
12/// Resolve the path to a project's auto-memory file:
13/// `<config_dir>/projects/<project_key>/memory/MEMORY.md`.
14///
15/// Always returns a resolved path; the caller decides whether the
16/// file exists.
17pub fn project_memory_path(config_dir: &Path, cwd: &Path) -> PathBuf {
18    let key =
19        crate::userdata::catalog::scan::project_key_for_directory(Some(&cwd.to_string_lossy()));
20    forge_sdk::projects_dir_for(config_dir).join(key).join("memory").join("MEMORY.md")
21}
22
23#[cfg(test)]
24mod tests {
25    use super::*;
26    use std::path::PathBuf;
27
28    #[test]
29    fn project_memory_path_uses_project_key_layout() {
30        let config_dir = PathBuf::from("/tmp/forge_test_memory_cfg");
31        let path = project_memory_path(&config_dir, Path::new("/tmp/some/proj"));
32        let expected: PathBuf =
33            ["projects", "-tmp-some-proj", "memory", "MEMORY.md"].iter().collect();
34        let expected_full = config_dir.join(expected);
35        assert_eq!(path, expected_full);
36    }
37
38    #[test]
39    fn project_memory_path_drops_no_leading_dash() {
40        // The CLI's project-key layout keeps the leading dash from
41        // absolute paths starting with `/`. Earlier TUI code stripped
42        // it, which produced a different directory than the CLI uses
43        // and thus never found the memory file. Pin the behaviour.
44        let config_dir = PathBuf::from("/tmp/forge_test_memory_cfg2");
45        let path = project_memory_path(&config_dir, Path::new("/Users/me/proj"));
46        let path_str = path.to_string_lossy();
47        assert!(
48            path_str.contains("/projects/-Users-me-proj/memory/"),
49            "expected `-Users-me-proj` (leading dash kept), got {path_str}"
50        );
51    }
52}