magi-code 0.64.0

Repository-aware CLI coding agent for terminal work
Documentation
use std::{
    collections::HashMap,
    path::{Path, PathBuf},
    process::Command,
    sync::{LazyLock, Mutex},
};

// Worktree topology is treated as stable for process lifetime; a new process rediscovers it.
static SCOPE_CACHE: LazyLock<Mutex<HashMap<PathBuf, PathBuf>>> =
    LazyLock::new(|| Mutex::new(HashMap::new()));

pub(crate) fn resolve_session_scope_path(cwd: &Path) -> PathBuf {
    let mut cache = SCOPE_CACHE.lock().expect("session scope cache poisoned");
    resolve_session_scope_path_with_cache(cwd, &mut cache, &mut run_git_command)
}

fn resolve_session_scope_path_with_cache(
    cwd: &Path,
    cache: &mut HashMap<PathBuf, PathBuf>,
    run_git: &mut impl FnMut(&Path, &[&str]) -> Option<GitCommandResult>,
) -> PathBuf {
    if let Some(path) = cache.get(cwd) {
        return path.clone();
    }

    let path = discover_session_scope_path(cwd, run_git);
    cache.insert(cwd.to_path_buf(), path.clone());
    path
}

fn discover_session_scope_path(
    cwd: &Path,
    run_git: &mut impl FnMut(&Path, &[&str]) -> Option<GitCommandResult>,
) -> PathBuf {
    if is_inside_git_worktree(cwd, run_git)
        && let Some(path) = primary_worktree_path(cwd, run_git)
    {
        return canonicalize_or_original(path);
    }
    cwd.to_path_buf()
}

fn run_git_command(cwd: &Path, args: &[&str]) -> Option<GitCommandResult> {
    let output = Command::new("git")
        .args(["-C"])
        .arg(cwd)
        .args(args)
        .output()
        .ok()?;
    Some(GitCommandResult {
        success: output.status.success(),
        stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
    })
}

struct GitCommandResult {
    success: bool,
    stdout: String,
}

fn is_inside_git_worktree(
    cwd: &Path,
    run_git: &mut impl FnMut(&Path, &[&str]) -> Option<GitCommandResult>,
) -> bool {
    let Some(output) = run_git(cwd, &["rev-parse", "--is-inside-work-tree"]) else {
        return false;
    };
    output.success && output.stdout.trim() == "true"
}

fn primary_worktree_path(
    cwd: &Path,
    run_git: &mut impl FnMut(&Path, &[&str]) -> Option<GitCommandResult>,
) -> Option<PathBuf> {
    let output = run_git(cwd, &["worktree", "list", "--porcelain"])?;
    if !output.success {
        return None;
    }
    output
        .stdout
        .lines()
        .find_map(|line| line.strip_prefix("worktree ").map(PathBuf::from))
}

fn canonicalize_or_original(path: PathBuf) -> PathBuf {
    path.canonicalize().unwrap_or(path)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::Cell;
    use tempfile::TempDir;

    fn result(success: bool, stdout: &str) -> GitCommandResult {
        GitCommandResult {
            success,
            stdout: stdout.to_owned(),
        }
    }

    #[test]
    fn non_git_directory_returns_literal_cwd() {
        let temp = TempDir::new().unwrap();
        let cwd = temp.path().join("plain");
        std::fs::create_dir(&cwd).unwrap();
        let mut cache = HashMap::new();
        let mut run_git = |_: &Path, _: &[&str]| None;

        assert_eq!(
            resolve_session_scope_path_with_cache(&cwd, &mut cache, &mut run_git),
            cwd
        );
    }

    #[test]
    fn git_command_failure_returns_cwd() {
        let temp = TempDir::new().unwrap();
        let cwd = temp.path().join("missing");
        let mut cache = HashMap::new();
        let mut run_git = |_: &Path, _: &[&str]| Some(result(false, ""));

        assert_eq!(
            resolve_session_scope_path_with_cache(&cwd, &mut cache, &mut run_git),
            cwd
        );
    }

    #[test]
    fn primary_worktree_is_canonicalized() {
        let temp = TempDir::new().unwrap();
        let cwd = temp.path().join("linked");
        let primary = temp.path().join("repo");
        std::fs::create_dir(&cwd).unwrap();
        std::fs::create_dir(&primary).unwrap();
        let mut cache = HashMap::new();
        let mut run_git = |_: &Path, args: &[&str]| {
            Some(if args[0] == "rev-parse" {
                result(true, "true\n")
            } else {
                result(true, &format!("worktree {}\n", primary.display()))
            })
        };

        assert_eq!(
            resolve_session_scope_path_with_cache(&cwd, &mut cache, &mut run_git),
            primary.canonicalize().unwrap()
        );
    }

    #[test]
    fn linked_worktree_fixture_preserves_primary_path() {
        let temp = TempDir::new().unwrap();
        let cwd = temp.path().join("linked");
        let primary = temp.path().join("repo");
        std::fs::create_dir(&cwd).unwrap();
        let mut cache = HashMap::new();
        let mut run_git = |_: &Path, args: &[&str]| {
            Some(if args[0] == "rev-parse" {
                result(true, "true\n")
            } else {
                result(
                    true,
                    &format!(
                        "worktree {}\nworktree {}\n",
                        primary.display(),
                        cwd.display()
                    ),
                )
            })
        };

        assert_eq!(
            resolve_session_scope_path_with_cache(&cwd, &mut cache, &mut run_git),
            primary
        );
    }

    #[test]
    fn missing_primary_path_uses_literal_path() {
        let temp = TempDir::new().unwrap();
        let cwd = temp.path().join("linked");
        let primary = temp.path().join("missing-primary");
        let mut cache = HashMap::new();
        let mut run_git = |_: &Path, args: &[&str]| {
            Some(if args[0] == "rev-parse" {
                result(true, "true")
            } else {
                result(true, &format!("worktree {}\n", primary.display()))
            })
        };

        assert_eq!(
            resolve_session_scope_path_with_cache(&cwd, &mut cache, &mut run_git),
            primary
        );
    }

    #[test]
    fn repeated_resolution_uses_cached_result() {
        let temp = TempDir::new().unwrap();
        let cwd = temp.path().join("repo");
        let primary = temp.path().join("primary");
        let calls = Cell::new(0);
        let mut cache = HashMap::new();
        let mut run_git = |_: &Path, args: &[&str]| {
            calls.set(calls.get() + 1);
            Some(if args[0] == "rev-parse" {
                result(true, "true")
            } else {
                result(true, &format!("worktree {}\n", primary.display()))
            })
        };

        let first = resolve_session_scope_path_with_cache(&cwd, &mut cache, &mut run_git);
        let second = resolve_session_scope_path_with_cache(&cwd, &mut cache, &mut run_git);

        assert_eq!(first, second);
        assert_eq!(calls.get(), 2);
    }
}