oxibrain-cli 0.2.0

Command-line interface and MCP server for oxibrain
use oxibrain::Brain;
use oxibrain::BrainConfig;
use oxibrain_core::TargetId;
use std::path::Path;

pub async fn run(dir: &Path, question: &str, space: &str) -> anyhow::Result<()> {
    let brain = Brain::open(BrainConfig::at(dir)).await?;
    let space_id = brain.ensure_space(space).await?;
    let q = oxibrain_core::Query {
        text: question.to_string(),
        mode: oxibrain_core::QueryMode::Hybrid,
        space: space_id,
        as_of: None,
        limit: 20,
        min_confidence: 0.0,
    };
    let result = brain.query(q).await?;
    println!(
        "hits: {} (total candidates: {})",
        result.items.len(),
        result.total_candidates
    );
    for item in &result.items {
        let target = match &item.target {
            TargetId::Episode { id } => format!("episode:{id}"),
            TargetId::Statement { id } => format!("statement:{id}"),
            TargetId::Entity { id } => format!("entity:{id}"),
            TargetId::Chunk { id } => format!("chunk:{id}"),
            TargetId::Community { id } => format!("community:{id}"),
        };
        println!(
            "  rank={} score={:.4} salience={:.4} -> {target}",
            item.rank, item.fused_score, item.salience
        );
        if let TargetId::Episode { id } = &item.target {
            if let Ok(Some(ep)) = brain.get_episode(id).await {
                let flat: String = ep.content.chars().filter(|c| *c != '\n').collect();
                let excerpt: String = flat.chars().take(160).collect();
                println!("    {excerpt}");
            }
        }
    }
    Ok(())
}