Skip to main content

clawdesk-skills

Skill system for dynamically extending agent capabilities. Skills are modular, hot-reloadable units of functionality that can be activated/deactivated at runtime. The skill selector matches incoming messages to relevant skills.

Dependencies

Internal: clawdesk-types, clawdesk-domain

External: tokio, async-trait, serde, tracing, thiserror

Modules

ModuleDescription
definitionSkill struct and metadata schema
registrySkillRegistry — tracks available skills and their state
resolverResolves skill locations from filesystem
loaderLoads skill definitions from TOML/YAML files
selectorMatches messages to relevant skills based on triggers

Key Types

/// Skill definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Skill {
pub id: String,
pub name: String,
pub version: String,
pub description: String,
pub triggers: Vec<SkillTrigger>,
pub tools: Vec<ToolDefinition>,
pub system_prompt_addition: Option<String>,
pub active: bool,
}

/// How a skill is triggered
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SkillTrigger {
Keyword(Vec<String>),
Regex(String),
Intent(String),
Always,
}

/// Skill registry managing available skills
pub struct SkillRegistry {
skills: Arc<RwLock<HashMap<String, Skill>>>,
}

impl SkillRegistry {
pub async fn register(&self, skill: Skill) -> Result<(), SkillError> { /* ... */ }
pub async fn activate(&self, id: &str) -> Result<(), SkillError> { /* ... */ }
pub async fn deactivate(&self, id: &str) -> Result<(), SkillError> { /* ... */ }
pub async fn reload_all(&self) -> Result<usize, SkillError> { /* ... */ }
pub async fn list(&self) -> Vec<Skill> { /* ... */ }
}

/// Matches messages to relevant skills
pub struct SkillSelector {
registry: Arc<SkillRegistry>,
}

impl SkillSelector {
pub async fn select(&self, message: &Message) -> Vec<Skill> {
// Returns all skills whose triggers match the message
}
}

Skill Definition File

# skills/web-search.toml
id = "web-search"
name = "Web Search"
version = "1.0.0"
description = "Search the web using multiple providers"
active = true

[[triggers]]
type = "keyword"
values = ["search", "find", "look up", "google"]

[[triggers]]
type = "intent"
value = "information_retrieval"

[[tools]]
name = "web_search"
description = "Search the web for information"
[tools.parameters]
type = "object"
properties = { query = { type = "string" } }
required = ["query"]

Example Usage

use clawdesk_skills::{SkillRegistry, SkillSelector, Skill};

let registry = Arc::new(SkillRegistry::new());

// Load skills from directory
let loader = SkillLoader::new("./skills/");
let skills = loader.load_all()?;
for skill in skills {
registry.register(skill).await?;
}

// Select skills for a message
let selector = SkillSelector::new(registry.clone());
let matched = selector.select(&message).await;

// Activate/deactivate at runtime
registry.activate("web-search").await?;
registry.deactivate("code-execution").await?;

// Hot-reload all skills
let count = registry.reload_all().await?;
info

Skills inject tools and system prompt additions into the agent pipeline dynamically. Only active skills participate in message processing.