collet 0.1.0

Relentless agentic coding orchestrator with zero-drop agent loops
Documentation
use std::sync::Arc;

use serde::Deserialize;

use crate::common::Result;
use crate::rag::RagManager;

#[derive(Debug, Deserialize)]
pub struct RagSearchInput {
    pub query: String,
    #[serde(default)]
    pub project: Option<String>,
    #[serde(default = "default_max_results")]
    pub max_results: usize,
}

fn default_max_results() -> usize {
    5
}

pub fn definition() -> serde_json::Value {
    serde_json::json!({
        "type": "function",
        "function": {
            "name": "rag_search",
            "description": "Search project documentation via RAG (Retrieval-Augmented Generation). Returns relevant document chunks from configured knowledge sources (Alcove docs, HTTP bridge). Use when you need external documentation, design specs, or project context beyond the codebase.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Natural language search query"
                    },
                    "project": {
                        "type": "string",
                        "description": "Project name to scope the search (optional, defaults to current project)"
                    },
                    "max_results": {
                        "type": "integer",
                        "description": "Maximum number of results to return (default: 5)"
                    }
                },
                "required": ["query"]
            }
        }
    })
}

/// Execute a RAG search and optionally post results to SharedKnowledge.
///
/// In swarm mode (Hive/Flock), discovered documents are posted as facts
/// so other agents can discover them via `build_relevant_summary()`.
pub async fn execute(
    input: RagSearchInput,
    rag_manager: &Arc<RagManager>,
    working_dir: &str,
    shared_knowledge: Option<&crate::agent::swarm::knowledge::SharedKnowledge>,
) -> Result<String> {
    let chunks = rag_manager
        .retrieve(
            &input.query,
            input.project.as_deref().or(Some(working_dir)),
            input.max_results,
        )
        .await;

    if chunks.is_empty() {
        return Ok("No relevant documents found.".to_string());
    }

    let mut output = String::new();
    for chunk in &chunks {
        let entry = format!(
            "## [{}] {} (score: {:.2})\n{}\n\n",
            chunk.adapter, chunk.source, chunk.score, chunk.content,
        );
        output.push_str(&entry);

        // Post to SharedKnowledge if in swarm mode
        if let Some(kb) = shared_knowledge {
            let topic = format!("rag:{}", chunk.source);
            kb.post_fact("rag", &topic, &chunk.content).await;
        }
    }

    Ok(output)
}