rab-agent 0.1.0

rab is a lightweight, extensible, Rust-based coding agent.
Documentation
use rab::agent::extension::{Cancel, Extension};
use rab::builtin::read::ReadExtension;

fn tmp_dir() -> std::path::PathBuf {
    let d = std::env::temp_dir().join(format!("rab-test-{}", uuid::Uuid::new_v4()));
    std::fs::create_dir_all(&d).unwrap();
    d
}

#[tokio::test]
async fn reads_file_content() {
    let tmp = tmp_dir();
    let path = tmp.join("test.txt");
    std::fs::write(&path, "hello world\nline two\n").unwrap();

    let ext = ReadExtension::new(tmp.clone());
    let tools = ext.tools();
    let tool = &tools[0];

    let result = tool
        .execute(
            "id".into(),
            serde_json::json!({"path": path.to_str().unwrap()}),
            Cancel::new(),
            None,
        )
        .await
        .unwrap();
    assert!(result.content.contains("hello world"));
    assert!(result.content.contains("line two"));
}

#[tokio::test]
async fn read_respects_offset() {
    let tmp = tmp_dir();
    let path = tmp.join("test.txt");
    let lines: Vec<String> = (1..=10).map(|i| format!("line {}", i)).collect();
    std::fs::write(&path, lines.join("\n")).unwrap();

    let ext = ReadExtension::new(tmp.clone());
    let tools = ext.tools();
    let tool = &tools[0];

    let result = tool
        .execute(
            "id".into(),
            serde_json::json!({"path": path.to_str().unwrap(), "offset": 5}),
            Cancel::new(),
            None,
        )
        .await
        .unwrap();

    assert!(
        result.content.contains("line 5"),
        "should contain line 5: {}",
        result.content
    );
    assert!(
        !result.content.lines().any(|l| l == "line 1"),
        "should not contain line 1: {}",
        result.content
    );
}

#[tokio::test]
async fn read_respects_limit() {
    let tmp = tmp_dir();
    let path = tmp.join("test.txt");
    let lines: Vec<String> = (1..=10).map(|i| format!("line {}", i)).collect();
    std::fs::write(&path, lines.join("\n")).unwrap();

    let ext = ReadExtension::new(tmp.clone());
    let tools = ext.tools();
    let tool = &tools[0];

    let result = tool
        .execute(
            "id".into(),
            serde_json::json!({"path": path.to_str().unwrap(), "offset": 1, "limit": 3}),
            Cancel::new(),
            None,
        )
        .await
        .unwrap();

    assert!(result.content.contains("line 1"));
    assert!(result.content.contains("line 3"));
    assert!(!result.content.contains("line 4"));
}

#[tokio::test]
async fn read_nonexistent_file_errors() {
    let tmp = tmp_dir();
    let ext = ReadExtension::new(tmp.clone());
    let tools = ext.tools();
    let tool = &tools[0];

    let result = tool
        .execute(
            "id".into(),
            serde_json::json!({"path": "nonexistent.txt"}),
            Cancel::new(),
            None,
        )
        .await;
    assert!(result.is_err());
}