ai-agent 0.13.4

Idiomatic agent sdk inspired by the claude code source leak
Documentation
// Source: /data/home/swei/claudecode/openclaudecode/src/utils/cwd.ts
//! Working directory utilities with async context support

#![allow(dead_code)]

use std::cell::Cell;
use std::path::PathBuf;

/// Thread-local storage for cwd override
thread_local! {
    static CWD_OVERRIDE: Cell<Option<String>> = const { Cell::new(None) };
}

/// Global current working directory (set during bootstrap)
static CURRENT_CWD: once_cell::sync::Lazy<std::sync::Mutex<Option<String>>> =
    once_cell::sync::Lazy::new(|| std::sync::Mutex::new(None));

/// Original working directory (before any overrides)
static ORIGINAL_CWD: once_cell::sync::Lazy<std::sync::Mutex<Option<String>>> =
    once_cell::sync::Lazy::new(|| {
        std::sync::Mutex::new(
            std::env::current_dir()
                .ok()
                .map(|p| p.to_string_lossy().to_string()),
        )
    });

/// Run a function with an overridden working directory for the current async context.
/// All calls to pwd()/get_cwd() within the function (and its async descendants) will
/// return the overridden cwd instead of the global one. This enables concurrent
/// agents to each see their own working directory without affecting each other.
pub fn run_with_cwd_override<T, F>(cwd: String, f: F) -> T
where
    F: FnOnce() -> T,
{
    CWD_OVERRIDE.with(|override_cell| {
        let old = override_cell.replace(Some(cwd));
        let result = f();
        override_cell.replace(old);
        result
    })
}

// Note: Async version would require tokio's async context propagation.
// For now, the sync version handles most use cases. Async version can be
// added later if needed using async_executors or similar.

/// Get the current working directory
pub fn pwd() -> String {
    // Try to get from thread-local override first
    let override_cwd = CWD_OVERRIDE.with(|cell| {
        let val = cell.replace(None);
        cell.set(val.clone());
        val
    });

    if let Some(cwd) = override_cwd {
        return cwd;
    }

    // Fall back to global cwd state
    if let Some(cwd) = get_cwd_state() {
        return cwd;
    }

    // Fall back to current directory
    std::env::current_dir()
        .map(|p| p.to_string_lossy().to_string())
        .unwrap_or_else(|_| ".".to_string())
}

/// Get the current working directory or the original working directory if the current one is not available
pub fn get_cwd() -> String {
    pwd()
}

/// Set the global current working directory (called during bootstrap)
pub fn set_cwd(cwd: PathBuf) {
    if let Ok(mut guard) = CURRENT_CWD.lock() {
        *guard = Some(cwd.to_string_lossy().to_string());
    }
}

/// Get the global current working directory state
fn get_cwd_state() -> Option<String> {
    CURRENT_CWD.lock().ok().and_then(|guard| guard.clone())
}

/// Get the original working directory
pub fn get_original_cwd() -> String {
    ORIGINAL_CWD
        .lock()
        .ok()
        .and_then(|guard| guard.clone())
        .unwrap_or_else(|| {
            std::env::current_dir()
                .map(|p| p.to_string_lossy().to_string())
                .unwrap_or_else(|_| ".".to_string())
        })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_pwd_without_override() {
        let cwd = pwd();
        assert!(!cwd.is_empty());
    }

    #[test]
    fn test_run_with_cwd_override() {
        let result = run_with_cwd_override("/test/dir".to_string(), || pwd());
        assert_eq!(result, "/test/dir");
    }

    #[test]
    fn test_get_original_cwd() {
        let orig = get_original_cwd();
        assert!(!orig.is_empty());
    }
}