Skip to main content

weavatrix_rust/operations/catalog/
mod.rs

1use blazingly_json::{Map, Value, json};
2use serde::Serialize;
3
4mod definitions;
5mod profile;
6mod schema;
7mod validation;
8
9pub use profile::ToolProfile;
10
11#[derive(Debug, Clone, Serialize)]
12#[serde(rename_all = "camelCase")]
13pub struct ToolDefinition {
14    pub name: &'static str,
15    pub description: &'static str,
16    pub input_schema: Value,
17}
18
19#[must_use]
20pub fn catalog() -> Vec<ToolDefinition> {
21    definitions::SPECS
22        .iter()
23        .filter(|spec| capability_is_compiled(spec.name))
24        .map(|spec| tool(spec.name, spec.description, spec.required))
25        .collect()
26}
27
28#[must_use]
29pub fn catalog_for_profile(profile: ToolProfile) -> Vec<ToolDefinition> {
30    catalog()
31        .into_iter()
32        .filter(|tool| profile.allows(tool.name))
33        .collect()
34}
35
36#[allow(clippy::needless_bool)]
37fn capability_is_compiled(tool: &str) -> bool {
38    if tool == "find_duplicates" {
39        cfg!(feature = "clone")
40    } else if matches!(
41        tool,
42        "change_impact"
43            | "git_history"
44            | "cross_repo_git"
45            | "verified_change"
46            | "graph_diff"
47            | "select_tests"
48    ) {
49        cfg!(feature = "git")
50    } else if tool == "search_code" {
51        cfg!(feature = "search")
52    } else if matches!(tool, "semantic_link" | "seo_link_suggestions") {
53        cfg!(feature = "semantic")
54    } else if tool == "vector_search" {
55        cfg!(feature = "vector")
56    } else if tool == "memory_context" {
57        cfg!(feature = "memory")
58    } else {
59        true
60    }
61}
62
63fn tool(
64    tool_name: &'static str,
65    description: &'static str,
66    required: &[&'static str],
67) -> ToolDefinition {
68    let mut properties = Map::from_iter([(
69        "output_format".to_owned(),
70        json!({
71            "type": "string",
72            "enum": ["text", "json", "structured"],
73            "default": "json",
74            "description": "text returns the concise text block only; json returns \
75                            structured output and mirrors it into text for clients that \
76                            read only content; structured drops that mirror, which is the \
77                            larger copy, and is safe only where the client reads \
78                            structuredContent"
79        }),
80    )]);
81    for name in schema::optional_fields(tool_name) {
82        properties.insert((*name).to_owned(), schema::field_schema(tool_name, name));
83    }
84    for name in required {
85        let schema = schema::field_schema(tool_name, name);
86        properties.insert((*name).to_owned(), schema);
87    }
88    let required = required
89        .iter()
90        .map(|value| json!(value))
91        .collect::<Vec<_>>();
92    ToolDefinition {
93        name: tool_name,
94        description,
95        input_schema: json!({
96            "type": "object",
97            "additionalProperties": true,
98            "properties": properties,
99            "required": required
100        }),
101    }
102}