sparrow-cli 0.4.0

A local-first Rust agent cockpit — route, run, replay, rewind
Documentation
use sparrow::event::{Block, RunId};
use sparrow::tools::builder_tools::LspClient;
use sparrow::tools::{Tool, ToolCtx, known_tool_metadata};

fn ctx(root: &std::path::Path) -> ToolCtx {
    ToolCtx {
        workspace_root: root.to_path_buf(),
        run_id: RunId("lsp-test".into()),
    }
}

fn text(result: sparrow::tools::ToolResult) -> String {
    result
        .content
        .into_iter()
        .map(|block| match block {
            Block::Text(text) => text,
            other => format!("{:?}", other),
        })
        .collect::<Vec<_>>()
        .join("\n")
}

#[tokio::test]
async fn lsp_goto_definition_and_references_are_real() {
    let tmp = tempfile::tempdir().expect("tmp");
    let src = tmp.path().join("src");
    std::fs::create_dir_all(&src).expect("src");
    std::fs::write(
        src.join("lib.rs"),
        "pub fn alpha() -> i32 { 7 }\n\npub fn beta() -> i32 { alpha() }\n",
    )
    .expect("write");

    let tool = LspClient;
    let def = tool
        .call(
            serde_json::json!({
                "action": "goto_definition",
                "file": "src/lib.rs",
                "symbol": "alpha"
            }),
            &ctx(tmp.path()),
        )
        .await
        .expect("definition");
    let def = text(def);
    assert!(def.contains("src/lib.rs:1: pub fn alpha()"));

    let refs = tool
        .call(
            serde_json::json!({
                "action": "find_references",
                "file": "src/lib.rs",
                "symbol": "alpha"
            }),
            &ctx(tmp.path()),
        )
        .await
        .expect("references");
    let refs = text(refs);
    assert!(refs.contains("src/lib.rs:1: pub fn alpha()"));
    assert!(refs.contains("src/lib.rs:3: pub fn beta()"));
}

#[tokio::test]
async fn lsp_hover_returns_context_and_symbol_definition() {
    let tmp = tempfile::tempdir().expect("tmp");
    std::fs::write(
        tmp.path().join("main.py"),
        "def greet(name):\n    return f'hi {name}'\n\nprint(greet('sparrow'))\n",
    )
    .expect("write");

    let out = LspClient
        .call(
            serde_json::json!({
                "action": "hover",
                "file": "main.py",
                "line": 4,
                "column": 8
            }),
            &ctx(tmp.path()),
        )
        .await
        .expect("hover");
    let out = text(out);
    assert!(out.contains("hover main.py:4:8"));
    assert!(out.contains("symbol: greet"));
    assert!(out.contains("main.py:1: def greet(name):"));
}

#[test]
fn lsp_is_advertised_as_debug_readonly_tool() {
    let tools = known_tool_metadata(None);
    let lsp = tools
        .into_iter()
        .find(|tool| tool.name == "lsp")
        .expect("lsp metadata");
    assert_eq!(lsp.toolset, "debug");
    assert!(!lsp.exec);
    assert!(!lsp.mutates_files);
}