diff --git a/src/config/skill_registry.rs b/src/config/skill_registry.rs index 2a58df8..bb97089 100644 --- a/src/config/skill_registry.rs +++ b/src/config/skill_registry.rs @@ -2,7 +2,7 @@ use super::role::{Role, RoleLike}; use super::skill::Skill; use super::skill_policy::SkillPolicy; -use anyhow::{Result, bail}; +use anyhow::{Result, anyhow, bail}; use indexmap::IndexMap; use std::collections::BTreeSet; @@ -24,12 +24,10 @@ impl SkillRegistry { Ok(()) } - pub fn unload(&mut self, name: &str) -> Result<()> { - if self.loaded.shift_remove(name).is_none() { - bail!("Skill '{name}' is not loaded"); - } - - Ok(()) + pub fn unload(&mut self, name: &str) -> Result { + self.loaded + .shift_remove(name) + .ok_or_else(|| anyhow!("Skill '{name}' is not loaded")) } pub fn loaded_names(&self) -> Vec { @@ -67,17 +65,13 @@ impl SkillRegistry { let mut effective = base.clone(); let skip_body = effective.is_embedded_prompt(); - let base_tools_set = effective.enabled_tools().is_some(); - let base_mcps_set = effective.enabled_mcp_servers().is_some(); + let base_tools = effective.enabled_tools(); + let base_tools_set = base_tools.is_some(); + let base_mcps = effective.enabled_mcp_servers(); + let base_mcps_set = base_mcps.is_some(); - let mut tools: BTreeSet = effective - .enabled_tools() - .map(|v| v.into_iter().collect()) - .unwrap_or_default(); - let mut mcps: BTreeSet = effective - .enabled_mcp_servers() - .map(|v| v.into_iter().collect()) - .unwrap_or_default(); + let mut tools: BTreeSet = base_tools.unwrap_or_default().into_iter().collect(); + let mut mcps: BTreeSet = base_mcps.unwrap_or_default().into_iter().collect(); for (name, skill) in &self.loaded { if !policy.allows(name) { diff --git a/src/function/skill.rs b/src/function/skill.rs index f8e759c..a53776c 100644 --- a/src/function/skill.rs +++ b/src/function/skill.rs @@ -212,12 +212,18 @@ async fn handle_unload(ctx: &mut RequestContext, args: &Value) -> Result _ => return Ok(json!({"error": "name is required"})), }; - if let Err(e) = ctx.skill_registry.unload(name) { - return Ok(json!({"error": e.to_string()})); - } + let skill = match ctx.skill_registry.unload(name) { + Ok(s) => s, + Err(e) => return Ok(json!({"error": e.to_string()})), + }; if let Err(e) = ctx.refresh_tool_scope(create_abort_signal()).await { - warn!("Unloaded skill '{name}' but failed to refresh tool scope: {e}"); + let _ = ctx.skill_registry.insert(skill); + return Ok(json!({ + "error": format!( + "Unloaded skill '{name}' but failed to refresh tool scope; restored: {e}" + ) + })); } Ok(json!({