bamboo-agent 2026.4.5

A fully self-contained AI agent backend framework with built-in web services, multi-LLM provider support, and comprehensive tool execution
Documentation
mod create;
mod list;
mod sessions;

pub use create::create_project;
pub use list::list_projects;
pub use sessions::get_project_sessions;

use std::path::{Path, PathBuf};

use super::fs::read_text_file;

fn read_project_path(project_dir: &Path) -> String {
    read_text_file(&project_dir.join(".project_path"), "project path")
        .unwrap_or_default()
        .trim()
        .to_string()
}

fn list_project_session_files(project_dir: &Path) -> Vec<PathBuf> {
    std::fs::read_dir(project_dir)
        .map(|entries| {
            entries
                .flatten()
                .map(|entry| entry.path())
                .filter(|path| {
                    path.is_file() && path.extension().and_then(|ext| ext.to_str()) == Some("jsonl")
                })
                .collect()
        })
        .unwrap_or_default()
}

fn filesystem_created_at(path: &Path) -> u64 {
    std::fs::metadata(path)
        .ok()
        .and_then(|meta| meta.created().ok())
        .and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok())
        .map(|duration| duration.as_secs())
        .unwrap_or(0)
}

fn project_id_from_canonical_path(path: &Path) -> String {
    path.to_string_lossy().replace(['/', '\\'], "-")
}

#[cfg(test)]
mod tests {
    use std::fs;

    use tempfile::tempdir;

    use super::{list_project_session_files, project_id_from_canonical_path, read_project_path};

    #[test]
    fn project_id_from_canonical_path_replaces_path_separators() {
        let id = project_id_from_canonical_path(std::path::Path::new("/tmp/workspace/demo"));
        assert_eq!(id, "-tmp-workspace-demo");
    }

    #[test]
    fn read_project_path_trims_whitespace() {
        let dir = tempdir().expect("temp dir");
        fs::write(dir.path().join(".project_path"), "/tmp/workspace\n")
            .expect("write .project_path");

        let project_path = read_project_path(dir.path());
        assert_eq!(project_path, "/tmp/workspace");
    }

    #[test]
    fn list_project_session_files_only_returns_jsonl_files() {
        let dir = tempdir().expect("temp dir");
        fs::write(dir.path().join("a.jsonl"), "{}\n").expect("write jsonl");
        fs::write(dir.path().join("b.txt"), "not session").expect("write txt");
        fs::create_dir_all(dir.path().join("nested.jsonl")).expect("create dir");

        let files = list_project_session_files(dir.path());
        let names: Vec<_> = files
            .iter()
            .filter_map(|path| path.file_name().and_then(|name| name.to_str()))
            .collect();

        assert_eq!(names, vec!["a.jsonl"]);
    }
}