robit_agent/tool/
load_skill.rs1use std::sync::Arc;
7
8use async_trait::async_trait;
9use serde::Deserialize;
10use serde_json::Value;
11
12use super::{Tool, ToolContext, ToolResult};
13use crate::error::Result;
14use crate::skill::SkillRegistry;
15
16pub struct LoadSkillTool {
17 skills: Arc<SkillRegistry>,
18}
19
20#[derive(Debug, Deserialize)]
21struct LoadSkillArgs {
22 skill_name: String,
24}
25
26impl LoadSkillTool {
27 pub fn new(skills: Arc<SkillRegistry>) -> Self {
28 Self { skills }
29 }
30}
31
32#[async_trait]
33impl Tool for LoadSkillTool {
34 fn name(&self) -> &str {
35 "load_skill"
36 }
37
38 fn description(&self) -> &str {
39 "Load detailed content of a specified skill. Returns skill metadata (name, description), full Markdown content, and source file path."
40 }
41
42 fn parameters_schema(&self) -> Value {
43 serde_json::json!({
44 "type": "object",
45 "properties": {
46 "skill_name": {
47 "type": "string",
48 "description": "Name of the skill to load (English name, exact match)"
49 }
50 },
51 "required": ["skill_name"]
52 })
53 }
54
55 fn requires_confirmation(&self) -> bool {
56 false
57 }
58
59 async fn execute(&self, args: Value, _ctx: &ToolContext) -> Result<ToolResult> {
60 let parsed: LoadSkillArgs = match serde_json::from_value(args) {
61 Ok(a) => a,
62 Err(e) => return Ok(ToolResult::error(format!("Argument parsing failed: {}", e))),
63 };
64
65 let skill_name = parsed.skill_name.trim();
66
67 match self.skills.get(skill_name) {
68 Some(skill) => {
69 let output = format!(
70 "## Skill: {name}\n\n\
71 **Description**: {description}\n\
72 **Version**: {version}\n\
73 **Trigger commands**: {triggers}\n\
74 **Source file**: {source_path}\n\n\
75 ---\n\n\
76 {content}",
77 name = skill.frontmatter.name,
78 description = skill.frontmatter.description,
79 version = skill.frontmatter.version,
80 triggers = if skill.frontmatter.triggers.is_empty() {
81 "(None)".to_string()
82 } else {
83 skill.frontmatter.triggers.join(", ")
84 },
85 source_path = skill.source_path.display(),
86 content = skill.content,
87 );
88 Ok(ToolResult::success(output))
89 }
90 None => {
91 let available: Vec<&str> = self.skills.skill_names();
92 Ok(ToolResult::error(format!(
93 "Skill '{}' does not exist. Available skills: {:?}",
94 skill_name, available
95 )))
96 }
97 }
98 }
99}