weavatrix-rust 2.1.0

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use blazingly_json::{Map, Value, json};
use serde::Serialize;

mod definitions;
mod profile;
mod schema;
mod validation;

pub use profile::ToolProfile;

#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolDefinition {
    pub name: &'static str,
    pub description: &'static str,
    pub input_schema: Value,
}

#[must_use]
pub fn catalog() -> Vec<ToolDefinition> {
    definitions::SPECS
        .iter()
        .filter(|spec| capability_is_compiled(spec.name))
        .map(|spec| tool(spec.name, spec.description, spec.required))
        .collect()
}

#[must_use]
pub fn catalog_for_profile(profile: ToolProfile) -> Vec<ToolDefinition> {
    catalog()
        .into_iter()
        .filter(|tool| profile.allows(tool.name))
        .collect()
}

#[allow(clippy::needless_bool)]
fn capability_is_compiled(tool: &str) -> bool {
    if tool == "find_duplicates" {
        cfg!(feature = "clone")
    } else if matches!(
        tool,
        "change_impact"
            | "git_history"
            | "cross_repo_git"
            | "verified_change"
            | "graph_diff"
            | "select_tests"
    ) {
        cfg!(feature = "git")
    } else if tool == "search_code" {
        cfg!(feature = "search")
    } else if matches!(tool, "semantic_link" | "seo_link_suggestions") {
        cfg!(feature = "semantic")
    } else if tool == "vector_search" {
        cfg!(feature = "vector")
    } else if tool == "memory_context" {
        cfg!(feature = "memory")
    } else {
        true
    }
}

fn tool(
    tool_name: &'static str,
    description: &'static str,
    required: &[&'static str],
) -> ToolDefinition {
    let mut properties = Map::from_iter([(
        "output_format".to_owned(),
        json!({
            "type": "string",
            "enum": ["text", "json"],
            "default": "json"
        }),
    )]);
    for name in schema::optional_fields(tool_name) {
        properties.insert((*name).to_owned(), schema::field_schema(tool_name, name));
    }
    for name in required {
        let schema = schema::field_schema(tool_name, name);
        properties.insert((*name).to_owned(), schema);
    }
    let required = required
        .iter()
        .map(|value| json!(value))
        .collect::<Vec<_>>();
    ToolDefinition {
        name: tool_name,
        description,
        input_schema: json!({
            "type": "object",
            "additionalProperties": true,
            "properties": properties,
            "required": required
        }),
    }
}