use serde::Serialize;
use crate::tool_catalog::TOOL_NAMES;
const TOOL_INDEX: &[(&str, &str, &str)] = &[
("create_scene", "Scenes", "Create a new scene"),
("get_scene_info", "Scenes", "Get scene metadata"),
("list_scenes", "Scenes", "List all scenes"),
("load_scene", "Scenes", "Load a scene"),
("save_scene", "Scenes", "Save the current scene"),
("start_scene_bake", "Scenes", "Start Lighting, legacy NavMesh, NavMeshSurface, or Occlusion baking"),
("get_scene_bake_status", "Scenes", "Poll bake progress, terminal failure, and verified saved artifacts"),
("create_gameobject", "GameObjects", "Create a new GameObject"),
("delete_gameobject", "GameObjects", "Delete a GameObject"),
("find_gameobject", "GameObjects", "Find GameObjects by name or criteria"),
("get_hierarchy", "GameObjects", "Get scene hierarchy tree"),
("modify_gameobject", "GameObjects", "Modify GameObject properties"),
("get_gameobject_details", "GameObjects", "Get detailed GameObject info"),
("add_component", "Components", "Add a component to a GameObject"),
("set_component_field", "Components", "Set a component field value"),
("get_component_types", "Components", "List available component types"),
("list_components", "Components", "List components on a GameObject"),
("modify_component", "Components", "Modify component properties"),
("remove_component", "Components", "Remove a component"),
("find_by_component", "Components", "Find GameObjects by component type"),
("get_component_values", "Components", "Get component field values"),
("get_object_references", "Components", "Get object reference graph"),
("create_animator_controller", "Animator", "Create an AnimatorController asset with parameters, states, and transitions"),
("create_animation_clip", "Animator", "Create an AnimationClip asset from sprite frames with frame rate and loop settings"),
("get_animation_curves", "Animator", "Read numeric bindings, keys and tangents; identify object-reference bindings"),
("edit_animation_curve", "Animator", "Set, upsert or remove numeric keys for one validated binding"),
("get_animator_runtime_info", "Animator", "Get Animator runtime info"),
("get_animator_state", "Animator", "Get current Animator state"),
("get_timeline", "Timeline", "Inspect a Timeline asset or PlayableDirector, tracks, clips, and bindings (read-only)"),
("manage_timeline", "Timeline", "Create and edit Timeline AnimationTracks, bind a director, or evaluate a time (mutating)"),
("create_prefab", "Prefabs", "Create a new Prefab"),
("exit_prefab_mode", "Prefabs", "Exit Prefab editing mode"),
("instantiate_prefab", "Prefabs", "Instantiate a Prefab in the scene"),
("modify_prefab", "Prefabs", "Modify Prefab properties"),
("open_prefab", "Prefabs", "Open a Prefab for editing"),
("save_prefab", "Prefabs", "Save Prefab changes"),
("analyze_scene_contents", "Assets", "Analyze scene asset contents"),
("manage_asset_database", "Assets", "Manage AssetDatabase operations"),
("analyze_asset_dependencies", "Assets", "Analyze asset dependency graph"),
("manage_asset_import_settings", "Assets", "Manage asset import settings"),
("create_sprite_atlas", "Assets", "Create a SpriteAtlas asset with packables and packing settings"),
("create_material", "Assets", "Create a new Material"),
("modify_material", "Assets", "Modify Material properties"),
("refresh_assets", "Assets", "Refresh the AssetDatabase"),
("vfx_describe_graph", "Visual Effect Graph", "Describe a Visual Effect Graph asset: contexts, blocks, operators, parameters, links, and compile errors"),
("vfx_list_library", "Visual Effect Graph", "List available Visual Effect Graph descriptors (`kind`: block, operator, context, parameter, or template)"),
("vfx_apply", "Visual Effect Graph", "Apply an authoring mutation to a Visual Effect Graph asset and recompile"),
("vfx_runtime", "Visual Effect Graph", "Control a VisualEffect component at runtime via its public API"),
("vfx_bake_sdf", "Visual Effect Graph", "Bake a Mesh asset into a Signed Distance Field Texture3D asset"),
("vfx_settings", "Visual Effect Graph", "Read or write VFX project settings or per-machine preferences"),
("addressables_analyze", "Addressables", "Analyze Addressables configuration"),
("addressables_build", "Addressables", "Build Addressables content"),
("addressables_manage", "Addressables", "Manage Addressables groups and entries"),
("get_compilation_state", "Code / LSP", "Get C# compilation state"),
("hot_reload_status", "Code / LSP", "Inspect hot reload preview state"),
("hot_reload", "Code / LSP", "Preview methods or recover Play"),
("read", "Code / LSP", "Read a C# source file"),
("find_refs", "Code / LSP", "Find symbol references"),
("search", "Code / LSP", "Search code by pattern"),
("find_symbol", "Code / LSP", "Find symbol definitions"),
("get_symbols", "Code / LSP", "Get symbols in a file"),
("build_index", "Code / LSP", "Build code search index"),
("update_index", "Code / LSP", "Update code search index"),
("get_index_status", "Code / LSP", "Get code search index status"),
("rename_symbol", "Code / LSP", "Rename a C# symbol"),
("replace_symbol_body", "Code / LSP", "Replace a C# symbol body"),
("insert_before_symbol", "Code / LSP", "Insert C# source before a symbol"),
("insert_after_symbol", "Code / LSP", "Insert C# source after a symbol"),
("remove_symbol", "Code / LSP", "Remove a C# symbol"),
("validate_text_edits", "Code / LSP", "Validate C# text edits"),
("write_csharp_file", "Code / LSP", "Write a complete C# source file"),
("create_csharp_file", "Code / LSP", "Create a new C# source file"),
("apply_csharp_edits", "Code / LSP", "Apply structured C# source edits"),
("create_class", "Code / LSP", "Create a C# class"),
("add_input_action", "Input System", "Add an Input Action"),
("create_action_map", "Input System", "Create an Action Map"),
("remove_action_map", "Input System", "Remove an Action Map"),
("remove_input_action", "Input System", "Remove an Input Action"),
("analyze_input_actions_asset", "Input System", "Analyze Input Actions asset"),
("get_input_actions_state", "Input System", "Get Input Actions runtime state"),
("add_input_binding", "Input System", "Add an Input Binding"),
("create_composite_binding", "Input System", "Create a composite binding"),
("remove_input_binding", "Input System", "Remove an Input Binding"),
("remove_all_bindings", "Input System", "Remove all bindings from an action"),
("manage_control_schemes", "Input System", "Manage control schemes"),
("input_gamepad", "Input System", "Simulate gamepad input"),
("input_keyboard", "Input System", "Simulate keyboard input"),
("input_mouse", "Input System", "Simulate mouse input"),
("input_touch", "Input System", "Simulate touch input"),
("create_input_sequence", "Input System", "Create an input sequence"),
("get_current_input_state", "Input System", "Get current input device state"),
("click_ui_element", "UI", "Click a UI element"),
("find_ui_elements", "UI", "Find UI elements by criteria"),
("get_ui_element_state", "UI", "Get UI element state"),
("set_ui_element_value", "UI", "Set UI element value"),
("simulate_ui_input", "UI", "Simulate UI input events"),
("pause_game", "Playback & Testing", "Pause Play mode"),
("play_game", "Playback & Testing", "Enter Play mode"),
("stop_game", "Playback & Testing", "Exit Play mode"),
("get_test_status", "Playback & Testing", "Get test run status"),
("run_tests", "Playback & Testing", "Run EditMode/PlayMode tests"),
("build_player", "Player Builds", "Queue a standalone player build on the Unity Editor host"),
("get_build_status", "Player Builds", "Read build status, report, and artifact paths by build ID"),
("profiler_get_metrics", "Profiler", "Get profiler metrics"),
("profiler_start", "Profiler", "Start profiler capture"),
("profiler_status", "Profiler", "Get profiler status"),
("profiler_stop", "Profiler", "Stop profiler capture"),
("clear_console", "Editor", "Clear the Console window"),
("clear_logs", "Editor", "Clear editor logs"),
("read_console", "Editor", "Read Console output"),
("manage_layers", "Editor", "Manage layers"),
("quit_editor", "Editor", "Quit Unity Editor"),
("manage_selection", "Editor", "Manage editor selection"),
("manage_tags", "Editor", "Manage tags"),
("manage_tools", "Editor", "Manage editor tools"),
("manage_windows", "Editor", "Manage editor windows"),
("execute_menu_item", "Editor", "Execute a menu item"),
("eval_csharp", "Editor", "Evaluate synchronous C#"),
("get_eval_status", "Editor", "Query evaluation result"),
("get_eval_stats", "Editor", "Query eval domain counters"),
("package_manager", "Editor", "Manage packages"),
("registry_config", "Editor", "Configure scoped registries"),
("get_editor_info", "Editor", "Get editor version info"),
("get_editor_state", "Editor", "Get editor state"),
("get_project_setting", "Editor", "Get a project setting"),
("set_project_setting", "Editor", "Set a project setting"),
("get_project_settings", "Editor", "Get project settings"),
("get_package_setting", "Editor", "Get a package setting"),
("set_package_setting", "Editor", "Set a package setting"),
("update_project_settings", "Editor", "Update project settings"),
("analyze_screenshot", "Screenshots & Video", "Analyze a screenshot"),
("capture_screenshot", "Screenshots & Video", "Capture a screenshot"),
("capture_video_start", "Screenshots & Video", "Start video capture"),
("capture_video_status", "Screenshots & Video", "Get video capture status"),
("capture_video_stop", "Screenshots & Video", "Stop video capture"),
("get_command_stats", "System", "Get bridge command statistics and, via the CLI, merged local transport timing stats"),
("ping", "System", "Check Unity Editor connectivity"),
("list_packages", "System", "List installed packages"),
("reference_fetch", "Reference Cache", "Shallow-clone UnityCsReference for the active Unity version into the local cache."),
("reference_status", "Reference Cache", "List cached UnityCsReference versions, branches, fetched-at, and disk usage."),
("reference_search", "Reference Cache", "Search the cached reference source for a pattern with optional path and result limits."),
("reference_grep", "Reference Cache", "Grep the cached reference source line-by-line with optional file glob and context lines."),
("reference_view", "Reference Cache", "Display a slice of a file in the cached reference source by line range."),
("reference_clean", "Reference Cache", "Remove old UnityCsReference snapshots, keeping the newest entries."),
("reference_find_symbol", "Reference Cache", "Look up type / method / property definitions in the cached reference source via a per-version on-disk index."),
("reference_diff", "Reference Cache", "Compare a symbol or path range between two cached Unity versions. Returns symbol-level hunks or `{added, removed, changed}`."),
("reference_resolve_symbol_at", "Reference Cache", "Resolve the identifier at a project cursor position (`Assets/...` / `Packages/...`) to candidate reference cache entries with view excerpts."),
("reference_embed_build", "Reference Cache", "Build an embedding index (BGE-Small-EN, ONNX) for a cached Unity version. Writes `.unity-cli-index/embeddings.bin`."),
("reference_embed_search", "Reference Cache", "Semantic / natural-language lookup over the embedding index. Returns hits sorted by cosine similarity."),
];
#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)]
pub struct ToolSummary {
pub name: &'static str,
pub description: &'static str,
}
#[derive(Debug, Default)]
pub struct ToolListFilter<'a> {
pub query: Option<&'a str>,
pub category: Option<&'a str>,
pub offset: usize,
pub limit: Option<usize>,
}
pub fn tool_categories() -> Vec<&'static str> {
let mut categories: Vec<&'static str> = Vec::new();
for (_, category, _) in TOOL_INDEX {
if !categories.contains(category) {
categories.push(category);
}
}
categories
}
pub fn category_slug(category: &str) -> String {
category
.split(|c: char| !c.is_ascii_alphanumeric())
.filter(|part| !part.is_empty())
.map(str::to_ascii_lowercase)
.collect::<Vec<_>>()
.join("-")
}
fn resolve_category(input: &str) -> Option<&'static str> {
let slug = category_slug(input);
tool_categories()
.into_iter()
.find(|category| category_slug(category) == slug)
}
fn index_entry(name: &str) -> Option<(&'static str, &'static str)> {
TOOL_INDEX
.iter()
.find(|(candidate, _, _)| *candidate == name)
.map(|(_, category, summary)| (*category, *summary))
}
pub fn filter_tools(filter: &ToolListFilter<'_>) -> Result<Vec<ToolSummary>, String> {
let category = match filter.category {
Some(input) => Some(resolve_category(input).ok_or_else(|| {
let valid: Vec<String> = tool_categories().into_iter().map(category_slug).collect();
format!(
"Unknown category `{input}`. Valid categories: {}",
valid.join(", ")
)
})?),
None => None,
};
let query = filter.query.map(str::to_ascii_lowercase);
let matches = TOOL_NAMES.iter().filter_map(|name| {
let (tool_category, summary) = index_entry(name).unwrap_or(("", ""));
if category.is_some_and(|expected| expected != tool_category) {
return None;
}
if let Some(query) = &query {
if !name.contains(query.as_str())
&& !summary.to_ascii_lowercase().contains(query.as_str())
{
return None;
}
}
Some(ToolSummary {
name,
description: summary,
})
});
Ok(matches
.skip(filter.offset)
.take(filter.limit.unwrap_or(usize::MAX))
.collect())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_tool_has_exactly_one_index_entry_with_a_short_summary() {
for name in TOOL_NAMES {
let entries: Vec<_> = TOOL_INDEX.iter().filter(|(n, _, _)| n == name).collect();
assert_eq!(entries.len(), 1, "{name} must have one index entry");
let summary = entries[0].2;
assert!(
!summary.is_empty() && summary.len() <= 160,
"{name}: {summary}"
);
}
assert_eq!(TOOL_INDEX.len(), TOOL_NAMES.len());
}
#[test]
fn tool_index_matches_docs_headings() {
let docs = std::fs::read_to_string(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/tools.md"))
.expect("docs/tools.md should exist");
let mut category: Option<String> = None;
let mut documented = Vec::new();
for line in docs.lines() {
if let Some(heading) = line.strip_prefix("### ") {
category = Some(heading.trim().to_string());
} else if let Some(heading) = line.strip_prefix("## ") {
category = heading
.starts_with("Reference Cache")
.then(|| "Reference Cache".to_string());
} else if let (Some(category), Some(rest)) = (&category, line.strip_prefix("| `")) {
let name = rest.split('`').next().unwrap_or_default();
if TOOL_NAMES.contains(&name) {
documented.push((name.to_string(), category.clone()));
}
}
}
assert_eq!(documented.len(), TOOL_NAMES.len());
for (name, category) in documented {
assert_eq!(
index_entry(&name).map(|(c, _)| c),
Some(category.as_str()),
"{name} category must match docs/tools.md"
);
}
}
#[test]
fn category_accepts_heading_or_slug() {
assert_eq!(resolve_category("scenes"), Some("Scenes"));
assert_eq!(
resolve_category("Playback & Testing"),
Some("Playback & Testing")
);
assert_eq!(
resolve_category("playback-testing"),
Some("Playback & Testing")
);
assert_eq!(resolve_category("code-lsp"), Some("Code / LSP"));
assert_eq!(resolve_category("nope"), None);
}
#[test]
fn query_matches_name_or_summary_case_insensitively() {
let found = filter_tools(&ToolListFilter {
query: Some("SCREENSHOT"),
..Default::default()
})
.unwrap();
let names: Vec<_> = found.iter().map(|tool| tool.name).collect();
assert_eq!(names, vec!["analyze_screenshot", "capture_screenshot"]);
}
#[test]
fn unknown_category_lists_valid_slugs() {
let error = filter_tools(&ToolListFilter {
category: Some("nope"),
..Default::default()
})
.unwrap_err();
assert!(
error.contains("scenes") && error.contains("reference-cache"),
"{error}"
);
}
}