use crate::skill::{SkillError, SkillRegistry};
use crate::tools::error::ToolError;
use crate::tools::registry::{Tool, ToolDefinition};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Clone)]
pub struct SkillTool {
registry: Arc<SkillRegistry>,
}
impl SkillTool {
pub fn new(registry: Arc<SkillRegistry>) -> Self {
Self { registry }
}
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct SkillArgs {
pub skill_name: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct SkillOutput {
pub instructions: String,
pub resources: Vec<String>,
pub skill_name: String,
}
impl Tool for SkillTool {
const NAME: &'static str = "load_skill";
type Args = SkillArgs;
type Output = SkillOutput;
type Error = ToolError;
async fn definition(&self, _prompt: String) -> ToolDefinition {
ToolDefinition {
name: Self::NAME.to_string(),
description: "Load a skill's instructions and discover available resources. \
Use this when a user's request matches a skill description from the system prompt."
.to_string(),
parameters: serde_json::json!({
"type": "object",
"properties": {
"skill_name": {
"type": "string",
"description": "Name of the skill to load"
}
},
"required": ["skill_name"]
}),
}
}
async fn call(&self, args: SkillArgs) -> Result<SkillOutput, ToolError> {
let skill = self.registry.get(&args.skill_name).ok_or_else(|| {
ToolError::Validation(format!("Skill not found: {}", args.skill_name))
})?;
let instructions = skill
.load_instructions()
.await
.map_err(|e| ToolError::Validation(format!("Failed to load instructions: {}", e)))?
.to_string();
let resources = skill
.list_resources()
.await
.map_err(|e| ToolError::Validation(format!("Failed to list resources: {}", e)))?;
Ok(SkillOutput {
instructions,
resources,
skill_name: args.skill_name,
})
}
}
impl From<SkillError> for ToolError {
fn from(e: SkillError) -> Self {
ToolError::Validation(e.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_skill_tool_name() {
let registry = Arc::new(SkillRegistry::new());
let tool = SkillTool::new(registry);
assert_eq!(tool.name(), "load_skill");
}
}