use crate::loader::SkillLoader;
use rucora_core::skill::SkillDefinition;
use serde_json::Value;
use std::collections::HashSet;
use std::path::Path;
use tracing::{debug, info};
pub struct SkillsAutoIntegrator {
loader: SkillLoader,
detected_tools: HashSet<String>,
}
impl SkillsAutoIntegrator {
pub fn new(skills_dir: impl AsRef<Path>) -> Self {
Self {
loader: SkillLoader::new(skills_dir),
detected_tools: HashSet::new(),
}
}
pub async fn load_and_analyze(
&mut self,
) -> Result<Vec<SkillDefinition>, Box<dyn std::error::Error>> {
let skills = self.loader.load_from_dir().await?;
info!("加载了 {} 个 Skills", skills.len());
for skill in &skills {
let required_tools = self.analyze_skill_requirements(skill);
for tool_name in &required_tools {
self.detected_tools.insert(tool_name.clone());
}
}
if !self.detected_tools.is_empty() {
info!("检测到需要的工具:{:?}", self.detected_tools);
}
Ok(skills)
}
fn analyze_skill_requirements(&self, skill: &SkillDefinition) -> Vec<String> {
let mut required_tools = Vec::new();
if let Some(metadata) = &skill.metadata
&& let Some(Value::Object(requires_map)) = metadata.get("requires")
{
if let Some(Value::Array(bins_array)) = requires_map.get("bins") {
for bin in bins_array {
if let Some(bin_name) = bin.as_str() {
required_tools.push(format!("cmd_{bin_name}"));
}
}
}
}
debug!("Skill {} 需要的工具:{:?}", skill.name, required_tools);
required_tools
}
pub fn detected_tools(&self) -> &HashSet<String> {
&self.detected_tools
}
pub fn loader(&self) -> &SkillLoader {
&self.loader
}
pub fn to_tool_descriptions(&self) -> Vec<Value> {
self.loader.to_tool_descriptions()
}
}
pub struct SkillToolAdapter {
skill_name: String,
description: String,
}
impl SkillToolAdapter {
pub fn new(skill: &SkillDefinition) -> Self {
Self {
skill_name: skill.name.clone(),
description: skill.description.clone(),
}
}
pub fn name(&self) -> &str {
&self.skill_name
}
pub fn description(&self) -> &str {
&self.description
}
}