Skill System
Domain-driven skill management with SKILL.md parsing, eligibility evaluation, prompt injection, and lifecycle management.
The skill module implements Skill System v2, a domain-driven runtime for managing skills. It handles skill registration, eligibility evaluation, SKILL.md parsing, prompt injection, and lifecycle management.
Design Philosophy
- Domain-driven — Skills are first-class domain objects with manifests, eligibility rules, and lifecycle states
- Declarative — Skills declare their capabilities in
SKILL.mdfiles; the system evaluates when to use them - Hot-reloadable — Skills can be added, updated, or removed without restarting
Core Components
SkillSystem
Unified facade for the rest of the application:
pub struct SkillSystem {
registry: SkillRegistry,
eligibility: EligibilityService,
installer: InstallExecutor,
}
impl SkillSystem {
pub async fn init(&self,
) -> Result<()> { /* ... */ }
pub async fn rebuild(&self,
) -> Result<()> { /* ... */ }
pub fn skill_status(&self,
) -> Vec<SkillStatusEntry> { /* ... */ }
}Determinism: skill_status() and full_status() return results sorted by skill ID.
SkillRegistry
Maintains the set of known skills:
pub struct SkillRegistry {
skills: HashMap<SkillId, SkillSnapshot>,
}Skill Manifest
Parsed from SKILL.md frontmatter:
pub struct SkillManifest {
pub id: SkillId,
pub name: String,
pub description: String,
pub triggers: Vec<String>,
pub eligibility: EligibilityRules,
}EligibilityService
Determines whether a skill can be used in the current context:
pub struct EligibilityService;
impl EligibilityService {
pub fn check(
&self,
skill: &SkillManifest,
context: &ExecutionContext,
) -> EligibilityResult { /* ... */ }
}Ineligibility reasons:
- Missing dependencies
- Unsupported platform
- Missing environment variables
- Conflicting skills
Skill Commands
Skills can expose CLI-style commands:
pub struct SkillCommandSpec {
pub name: String,
pub description: String,
pub args: Vec<CommandArg>,
}Resolution: When multiple skills define the same command name, workspace skills override bundled skills (sorted by priority, then ID for tie-breaking).
InstallExecutor
Installs skills from external sources:
pub struct InstallExecutor;
impl InstallExecutor {
pub async fn install(
&self,
spec: &InstallSpec,
) -> Result<InstallResult> { /* ... */ }
}Security: Uses strict allowlist for package names — only [a-zA-Z0-9\-_./:@+=~] permitted. Blocks spaces, quotes, redirections, backticks, $(), and all shell metacharacters.
Prompt Injection
Skills are injected into the system prompt as XML:
pub fn build_skills_prompt_xml(
skills: &[SkillSnapshot],
) -> String<skills>
<skill id="code-review" name="Code Review">
<triggers>
<trigger>review code</trigger>
<trigger>check this</trigger>
</triggers>
<instructions>
When reviewing code, check for: ...
</instructions>
</skill>
</skills>Lifecycle
Discovered → Evaluated → Eligible → Loaded → Active
↓
IneligibleStates:
Discovered— Found in filesystemEvaluated— Eligibility checkedEligible— Can be used in current contextLoaded— Parsed and readyActive— Currently available to the agent
Safety Properties
- No command injection — Strict allowlist for package names
- Deterministic ordering — Skills sorted by ID for reproducible output
- No unwrap in production — All error paths return
Result - Lock safety — Uses
tokio::sync::RwLock(no poisoning)
Code Location
src/skill/mod.rs—SkillSystemfacadesrc/skill/registry.rs— Skill registrysrc/skill/eligibility.rs— Eligibility evaluationsrc/skill/manifest.rs—SKILL.mdparsingsrc/skill/installer.rs— Skill installationsrc/skill/commands.rs— Command resolutionsrc/skill/prompt.rs— Prompt injectionsrc/skill/status.rs— Status trackingsrc/skill/snapshot.rs— Skill snapshots
See Also
- Skills — Conceptual overview of skills
- Extensions — Plugin architecture
- ClawHub — Skill registry integration