use crate::types::*;
use crate::skills::loader::{load_skills_from_dir, LoadedSkill};
use std::path::Path;
use std::sync::{OnceLock, Mutex};
use std::collections::HashMap;
static LOADED_SKILLS: OnceLock<Mutex<HashMap<String, LoadedSkill>>> = OnceLock::new();
fn init_skills_map() -> Mutex<HashMap<String, LoadedSkill>> {
let mut skills = HashMap::new();
if let Ok(loaded) = load_skills_from_dir(Path::new("examples/skills")) {
for skill in loaded {
skills.insert(skill.metadata.name.clone(), skill);
}
}
Mutex::new(skills)
}
fn get_skills_map() -> &'static Mutex<HashMap<String, LoadedSkill>> {
LOADED_SKILLS.get_or_init(init_skills_map)
}
pub fn get_skill(name: &str) -> Option<LoadedSkill> {
let guard = get_skills_map().lock().ok()?;
guard.get(name).cloned()
}
pub fn get_all_skill_names() -> Vec<String> {
let guard = get_skills_map().lock().ok();
guard.map(|g| g.keys().cloned().collect()).unwrap_or_default()
}
pub fn register_skills_from_dir(dir: &Path) {
if dir.as_os_str().is_empty() {
return;
}
if let Ok(loaded) = load_skills_from_dir(dir) {
if let Ok(mut skills) = get_skills_map().lock() {
for skill in loaded {
skills.insert(skill.metadata.name.clone(), skill);
}
}
}
}
pub fn register_skill(skill: LoadedSkill) {
if let Ok(mut skills) = get_skills_map().lock() {
skills.insert(skill.metadata.name.clone(), skill);
}
}
pub fn register_skills(skills: Vec<LoadedSkill>) {
if let Ok(mut map) = get_skills_map().lock() {
for skill in skills {
map.insert(skill.metadata.name.clone(), skill);
}
}
}
pub struct SkillTool;
impl SkillTool {
pub fn new() -> Self {
Self
}
pub fn input_schema(&self) -> ToolInputSchema {
ToolInputSchema {
schema_type: "object".to_string(),
properties: serde_json::json!({
"skill": {
"type": "string",
"description": "The name of the skill to invoke"
}
}),
required: Some(vec!["skill".to_string()]),
}
}
pub async fn execute(&self, input: serde_json::Value, _context: &ToolContext) -> Result<ToolResult, crate::error::AgentError> {
let skill_name = input["skill"].as_str().unwrap_or("");
if let Some(skill) = get_skill(skill_name) {
let content = format!(
"Skill '{}' loaded. Here is the skill content:\n\n{}\n\nYou can now use tools to complete the task.",
skill_name, skill.content
);
Ok(ToolResult {
result_type: "text".to_string(),
tool_use_id: "skill".to_string(),
content,
is_error: Some(false),
})
} else {
let available = get_all_skill_names();
Ok(ToolResult {
result_type: "text".to_string(),
tool_use_id: "skill".to_string(),
content: format!("Skill '{}' not found. Available skills: {:?}", skill_name, available),
is_error: Some(true),
})
}
}
}
impl Default for SkillTool {
fn default() -> Self {
Self::new()
}
}