//! Skills management methods use std::sync::Arc; use zclaw_types::Result; use super::Kernel; impl Kernel { /// Get the skills registry pub fn skills(&self) -> &Arc { &self.skills } /// List all discovered skills pub async fn list_skills(&self) -> Vec { self.skills.list().await } /// Refresh skills from a directory pub async fn refresh_skills(&self, dir: Option) -> Result<()> { if let Some(path) = dir { self.skills.add_skill_dir(path).await?; } else if let Some(ref skills_dir) = self.config.skills_dir { self.skills.add_skill_dir(skills_dir.clone()).await?; } Ok(()) } /// Get the configured skills directory pub fn skills_dir(&self) -> Option<&std::path::PathBuf> { self.config.skills_dir.as_ref() } /// Create a new skill in the skills directory pub async fn create_skill(&self, manifest: zclaw_skills::SkillManifest) -> Result<()> { let skills_dir = self.config.skills_dir.as_ref() .ok_or_else(|| zclaw_types::ZclawError::InvalidInput( "Skills directory not configured".into() ))?; self.skills.create_skill(skills_dir, manifest).await } /// Update an existing skill pub async fn update_skill( &self, id: &zclaw_types::SkillId, manifest: zclaw_skills::SkillManifest, ) -> Result { let skills_dir = self.config.skills_dir.as_ref() .ok_or_else(|| zclaw_types::ZclawError::InvalidInput( "Skills directory not configured".into() ))?; self.skills.update_skill(skills_dir, id, manifest).await } /// Delete a skill pub async fn delete_skill(&self, id: &zclaw_types::SkillId) -> Result<()> { let skills_dir = self.config.skills_dir.as_ref() .ok_or_else(|| zclaw_types::ZclawError::InvalidInput( "Skills directory not configured".into() ))?; self.skills.delete_skill(skills_dir, id).await } /// Execute a skill with the given ID and input pub async fn execute_skill( &self, id: &str, context: zclaw_skills::SkillContext, input: serde_json::Value, ) -> Result { // Inject LLM completer into context for PromptOnly skills let mut ctx = context; if ctx.llm.is_none() { ctx.llm = Some(self.llm_completer.clone()); } self.skills.execute(&zclaw_types::SkillId::new(id), &ctx, input).await } /// Generate a skill from an aggregated pattern and register it. /// /// Full pipeline: /// 1. Build LLM prompt from pattern /// 2. Call LLM to get JSON response /// 3. Parse response into SkillCandidate /// 4. Validate through QualityGate (threshold 0.85 for auto-mode) /// 5. Convert to SkillManifest (PromptOnly, disabled by default) /// 6. Persist to disk via SkillRegistry pub async fn generate_and_register_skill( &self, pattern: &zclaw_growth::pattern_aggregator::AggregatedPattern, ) -> Result { // 1. Build prompt let prompt = zclaw_growth::skill_generator::SkillGenerator::build_prompt(pattern); // 2. Call LLM let request = zclaw_runtime::driver::CompletionRequest { model: self.driver.provider().to_string(), system: Some("你是技能设计专家,只返回 JSON 格式的技能定义。".to_string()), messages: vec![zclaw_types::Message::user(prompt)], max_tokens: Some(1024), temperature: Some(0.3), stream: false, ..Default::default() }; let response = self.driver.complete(request).await?; let text = response.content.iter() .filter_map(|block| match block { zclaw_runtime::driver::ContentBlock::Text { text } => Some(text.as_str()), _ => None, }) .collect::>() .join(""); // 3. Parse into SkillCandidate let candidate = zclaw_growth::skill_generator::SkillGenerator::parse_response( &text, pattern, )?; // 4. Validate through QualityGate (higher threshold for auto-generation) let existing_triggers: Vec = self.skills.list().await .into_iter() .flat_map(|m| m.triggers) .collect(); let gate = zclaw_growth::quality_gate::QualityGate::new(0.85, existing_triggers); let report = gate.validate_skill(&candidate); if !report.passed { return Err(zclaw_types::ZclawError::ConfigError(format!( "QualityGate rejected: {}", report.issues.join("; ") ))); } // 5. Convert to SkillManifest (PromptOnly, disabled) let manifest = super::evolution_bridge::candidate_to_manifest(&candidate); let skill_id = manifest.id.to_string(); // 6. Persist to disk let skills_dir = self.config.skills_dir.as_ref() .ok_or_else(|| zclaw_types::ZclawError::InvalidInput( "Skills directory not configured".into() ))?; self.skills.create_skill(skills_dir, manifest).await?; tracing::info!( "[Kernel] Auto-generated skill '{}' (id={}) registered (disabled)", candidate.name, skill_id ); Ok(skill_id) } }