brainwires-skills
Agent skills system for the Brainwires Agent Framework.
Attribution
This crate implements the Agent Skills open standard, originally developed by Anthropic and published as an open specification on December 18, 2025. The Agent Skills format was designed by Barry Zhang, Keith Lazuka, and Mahesh Murag at Anthropic.
Official resources:
- Agent Skills Specification — the complete format specification
- anthropics/skills — Anthropic's official example skills repository
- agentskills/agentskills — the open standard repository and reference library
- Blog: Equipping agents for the real world with Agent Skills
brainwires-skills aims to faithfully implement the Agent Skills specification while adding Brainwires-specific extensions. All spec-compliant fields and validation rules are supported. Extensions beyond the spec are clearly documented in the Brainwires Extensions section below.
Overview
brainwires-skills implements the Agent Skills markdown-based format that extends agent capabilities through composable, reusable skill packages. Skills are defined in SKILL.md files with YAML frontmatter for metadata and markdown body for instructions. The crate provides parsing, registry management, keyword-based routing, and multi-mode execution — enabling agents to discover, match, and run skills on demand.
The system uses a progressive disclosure pattern (as defined by the spec): at startup, only lightweight metadata (name, description) is loaded for fast matching. Full skill content is loaded on-demand when a skill is activated, and cached for subsequent use. This enables fast startup and efficient memory usage even with hundreds of registered skills.
Design principles:
- Progressive disclosure — metadata loaded at startup (~100 bytes per skill), full instructions loaded lazily on activation and cached in memory
- Three execution modes — inline (instructions injected into conversation), subagent (background task via AgentPool), and script (Rhai script via OrchestratorTool)
- Skill hierarchy — personal (
~/.brainwires/skills/), project (.brainwires/skills/), and built-in skills; project skills override personal skills with the same name - Tool restrictions — optional
allowed-toolslist restricts which tools a skill can use during execution (accepts both YAML lists and spec-compliant space-delimited strings) - Template rendering — simple
{{arg}}substitution and{{#if var}}...{{/if}}conditionals for parameterized skills
┌───────────────────────────────────────────────────────────────────────┐
│ brainwires-skills │
│ │
│ SKILL.md Files │
│ │ │
│ ▼ │
│ ┌─── Parser ──────────────────────────────────────────────────────┐ │
│ │ parse_skill_metadata() → SkillMetadata (lightweight) │ │
│ │ parse_skill_file() → Skill (full content) │ │
│ │ render_template() → String (variable substitution) │ │
│ │ Validation: name (lowercase, hyphens, ≤64), desc (≤1024) │ │
│ └──────────────────────────────────────┬───────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─── Registry ────────────────────────────────────────────────────┐ │
│ │ discover_from(paths) → load metadata from directories │ │
│ │ get_metadata(name) → &SkillMetadata (fast lookup) │ │
│ │ get_skill(name) → &Skill (lazy load + cache) │ │
│ │ Hierarchy: Personal → Project → Builtin (later overrides) │ │
│ └──────────────────────────────────────┬───────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─── Router ──────────────────────────────────────────────────────┐ │
│ │ match_skills(query) → Vec<SkillMatch> (sorted by conf.) │ │
│ │ format_suggestions() → "The skill `/name` may help." │ │
│ │ explicit_match(name) → SkillMatch (confidence 1.0) │ │
│ │ Keyword matching: name(3x), name-words(2x), desc-words(1x) │ │
│ └──────────────────────────────────────┬───────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─── Executor ────────────────────────────────────────────────────┐ │
│ │ execute(skill, args) → SkillResult │ │
│ │ prepare_subagent(skill) → SubagentPrepared (task + prompt) │ │
│ │ prepare_script(skill) → ScriptPrepared (rendered script) │ │
│ │ filter_allowed_tools() → enforces tool restrictions │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
Quick Start
Add to your Cargo.toml:
[]
= "0.6"
Register and execute a skill:
use ;
use HashMap;
use Arc;
use RwLock;
async
Architecture
SKILL.md Format
Skills are defined as markdown files with YAML frontmatter per the Agent Skills specification:
name: review-pr
description: Reviews pull requests for code quality and security issues.
allowed-tools: Read Grep # spec format: space-delimited
license: MIT
compatibility: Requires git CLI
model: claude-sonnet-4 # Brainwires extension
metadata:
category: code-review
execution: subagent # Brainwires extension
hooks: # Brainwires extension
-
When reviewing a pull request:
1. 2.3.
allowed-tools also accepts YAML list format:
allowed-tools:
- Read
- Grep
Supported file layouts:
| Layout | Path | Description |
|---|---|---|
| Direct file | skills/review-pr.md |
Single .md file named after the skill |
| Subdirectory | skills/review-pr/SKILL.md |
Skill in its own directory |
SkillMetadata
Lightweight metadata loaded at startup for fast matching and display.
| Field | Type | Spec | Description |
|---|---|---|---|
name |
String |
Required | Skill identifier (lowercase, hyphens only, max 64 chars, no consecutive hyphens) |
description |
String |
Required | Used for semantic matching (max 1024 chars) |
allowed_tools |
Option<Vec<String>> |
Optional | Tool restrictions (None = all tools allowed). Accepts YAML list or space-delimited string |
license |
Option<String> |
Optional | Software license |
compatibility |
Option<String> |
Optional | Environment requirements (max 500 chars) |
metadata |
Option<HashMap<String, String>> |
Optional | Custom key-value pairs (category, execution, author, version) |
model |
Option<String> |
Extension | Model override for execution (Brainwires extension) |
hooks |
Option<Vec<String>> |
Extension | Lifecycle hook event subscriptions (Brainwires extension) |
source |
SkillSource |
Internal | Where the skill was loaded from |
source_path |
PathBuf |
Internal | File path for lazy loading |
| Method | Description |
|---|---|
new(name, description) |
Create metadata with defaults |
with_source(source) |
Set the source location (builder pattern) |
with_source_path(path) |
Set the file path (builder pattern) |
execution_mode() |
Get execution mode from metadata map |
get_metadata(key) |
Get a custom metadata value |
has_tool_restrictions() |
Check if skill has an allowed-tools list |
is_tool_allowed(tool_name) |
Check if a specific tool is permitted |
SkillSource
| Variant | Path | Description |
|---|---|---|
Personal |
~/.brainwires/skills/ |
User-level skills (default) |
Project |
.brainwires/skills/ |
Project-level skills (override personal) |
Builtin |
(bundled) | Skills shipped with the application |
SkillExecutionMode
| Variant | Description |
|---|---|
Inline |
Instructions injected into current conversation (default) |
Subagent |
Dedicated background agent spawned via AgentPool |
Script |
Rhai script executed via OrchestratorTool |
Skill
Full skill content loaded on-demand when activated.
| Field | Type | Description |
|---|---|---|
metadata |
SkillMetadata |
The lightweight metadata |
instructions |
String |
Full markdown body after frontmatter |
execution_mode |
SkillExecutionMode |
Derived from metadata |
| Method | Description |
|---|---|
new(metadata, instructions) |
Create from metadata and instruction content |
name() |
Get the skill name |
description() |
Get the skill description |
allowed_tools() |
Get the allowed tools list (if any) |
model() |
Get the model override (if any) |
runs_as_subagent() |
Check if execution mode is Subagent |
is_script() |
Check if execution mode is Script |
SkillResult
| Variant | Fields | Description |
|---|---|---|
Inline |
instructions, model_override |
Instructions for conversation injection |
Subagent |
agent_id |
Spawned agent identifier |
Script |
output, is_error |
Script execution output |
SkillRegistry
Central registry managing all available skills with progressive disclosure.
| Method | Description |
|---|---|
new() |
Create empty registry |
discover_from(paths) |
Clear and load metadata from (path, source) pairs |
reload() |
Reload using same paths as last discover_from |
register(metadata) |
Register a skill directly (for built-in skills) |
get_metadata(name) |
Get metadata by name → Option<&SkillMetadata> |
get_skill(name) |
Lazy load full skill (cached) → Result<&Skill> |
get_skill_mut(name) |
Mutable access with lazy load → Result<&mut Skill> |
contains(name) |
Check if a skill exists |
list_skills() |
All skill names, sorted alphabetically |
all_metadata() |
All metadata for matching |
skills_by_source(source) |
Filter skills by source |
skills_by_category(category) |
Filter by metadata category value |
len() / is_empty() |
Registry size |
clear_cache() |
Clear loaded cache, force disk reload on next access |
remove(name) |
Remove a skill from registry and cache |
format_skill_list() |
Display all skills grouped by source |
format_skill_detail(name) |
Display detailed info for a single skill |
SkillRouter
Handles skill activation through keyword matching against user queries. Skills are suggested to users, not auto-activated.
| Method | Description |
|---|---|
new(registry) |
Create router with shared registry reference |
with_min_confidence(confidence) |
Set minimum confidence threshold (default: 0.5) |
match_skills(query) |
Match query against descriptions → Vec<SkillMatch> (sorted by confidence) |
format_suggestions(matches) |
Format top 3 matches as suggestion text → Option<String> |
skill_exists(name) |
Check if a skill exists by name |
explicit_match(skill_name) |
Create explicit match (confidence 1.0, used for /skill-name) |
Keyword matching weights:
| Match type | Weight | Description |
|---|---|---|
| Full name match | +3 | Query contains skill name or vice versa |
| Name word match | +2 | Individual skill name words found in query |
| Description word match | +1 | Query words found in skill description |
Confidence formula: 0.6 + (match_count * 0.05), capped at 0.9.
MatchSource enum:
| Variant | Description |
|---|---|
Semantic |
Matched via semantic similarity (future enhancement) |
Keyword |
Matched via keyword patterns |
Explicit |
User explicitly invoked (/skill-name) |
SkillExecutor
Executes skills in one of three modes. Tool restrictions from allowed-tools are enforced during preparation.
| Method | Description |
|---|---|
new(registry) |
Create executor with shared registry reference |
execute_by_name(name, args) |
Load skill from registry and execute → Result<SkillResult> |
execute(skill, args) |
Execute a skill with template-rendered arguments → Result<SkillResult> |
prepare_subagent(skill, tools, args) |
Prepare subagent context → Result<SubagentPrepared> |
prepare_script(skill, tools, args) |
Prepare script execution → Result<ScriptPrepared> |
get_execution_mode(name) |
Look up execution mode by skill name |
SubagentPrepared:
| Field | Type | Description |
|---|---|---|
task_description |
String |
Rendered instructions |
allowed_tool_names |
Vec<String> |
Filtered tool list |
system_prompt |
String |
System prompt for the subagent |
model_override |
Option<String> |
Model override |
ScriptPrepared:
| Field | Type | Description |
|---|---|---|
script_content |
String |
Rendered Rhai script |
allowed_tool_names |
Vec<String> |
Filtered tool list |
model_override |
Option<String> |
Model override |
skill_name |
String |
Skill name for logging |
Parser
Functions for parsing SKILL.md files and rendering templates.
| Function | Description |
|---|---|
parse_skill_metadata(path) |
Parse only YAML frontmatter → Result<SkillMetadata> |
parse_skill_file(path) |
Parse complete file (metadata + instructions) → Result<Skill> |
render_template(template, args) |
Substitute {{arg}} placeholders and {{#if var}}...{{/if}} conditionals |
Skill name validation rules (per Agent Skills specification):
| Rule | Constraint |
|---|---|
| Characters | Lowercase letters, digits, and hyphens only |
| Length | 1-64 characters |
| Boundaries | Cannot start or end with a hyphen |
| Consecutive hyphens | Cannot contain -- |
| Directory match | Name should match parent directory name (warning if mismatched) |
| Examples | review-pr, commit, explain-code-123 |
Usage Examples
Discover and list skills
use ;
let mut registry = new;
registry.discover_from?;
// List all skills
let names = registry.list_skills;
println!;
// Display formatted listing (grouped by source)
println!;
// Filter by category
let dev_skills = registry.skills_by_category;
for skill in dev_skills
Route queries to skills
use ;
use Arc;
use RwLock;
let registry = new;
let router = new
.with_min_confidence;
// Match against user query
let matches = router.match_skills.await;
for m in &matches
// Format as user-facing suggestion
if let Some = router.format_suggestions
// Explicit invocation (user typed /review-pr)
let explicit = router.explicit_match;
assert_eq!;
Execute skills with template arguments
use ;
use HashMap;
use Arc;
use RwLock;
let registry = new;
let executor = new;
// Execute with template arguments
let mut args = new;
args.insert;
args.insert;
let result = executor.execute_by_name.await?;
match result
Prepare subagent execution with tool restrictions
use ;
use HashMap;
use Arc;
use RwLock;
let registry = new;
let executor = new;
let mut metadata = new;
metadata.allowed_tools = Some;
let skill = new;
let available_tools = vec!;
let mut args = new;
args.insert;
let prepared = executor.prepare_subagent.await?;
// Only Read and Grep are allowed
assert_eq!;
println!;
// Caller spawns via AgentPool with these constraints
Use template rendering directly
use render_template;
use HashMap;
let template = "Review {{file_path}}{{#if focus}} with focus on {{focus}}{{/if}}.";
// With all args
let mut args = new;
args.insert;
args.insert;
let result = render_template;
assert_eq!;
// Without optional arg
let mut args2 = new;
args2.insert;
args2.insert;
let result2 = render_template;
assert_eq!;
Parse skill files directly
use ;
use Path;
// Parse only metadata (fast, for startup)
let metadata = parse_skill_metadata?;
println!;
println!;
println!;
// Parse full skill (on activation)
let skill = parse_skill_file?;
println!;
println!;
Integration
Use via the brainwires facade crate with the skills feature, or depend on brainwires-skills directly:
# Via facade
[]
= { = "0.6", = ["skills"] }
# Direct
[]
= "0.6"
The crate re-exports all components at the top level:
use ;
Brainwires Extensions
This crate extends the Agent Skills specification with the following Brainwires-specific features. These are clearly marked as extensions and do not conflict with spec-compliant skills.
| Extension | Description |
|---|---|
model field |
Optional model override per skill (e.g., model: claude-sonnet-4). Overrides the default model when executing the skill. |
hooks field |
Optional lifecycle hook event subscriptions (e.g., hooks: [agent_started, tool_after_execute]). Registers the skill to fire on matching lifecycle events. |
| Execution modes | The metadata.execution key controls how a skill runs: inline (default, injected into conversation), subagent (spawned via AgentPool), or script (Rhai script via OrchestratorTool). |
| Flat file layout | Skills can be defined as a single file (skills/review-pr.md) in addition to the spec-defined subdirectory layout (skills/review-pr/SKILL.md). |
YAML list allowed-tools |
In addition to the spec's space-delimited string format, allowed-tools also accepts YAML lists for convenience. |
Skills that use only spec-defined fields (name, description, license, compatibility, metadata, allowed-tools) are fully portable across any Agent Skills-compatible agent.
License
Licensed under the MIT License. See LICENSE for details.