PulseBot's skills system provides three ways to extend the agent's capabilities:
- Built-in Skills - Python classes that register tools directly with the LLM (web search, file ops, shell)
- External Skills (agentskills.io) - Markdown-based skill packages discovered from the filesystem, following the agentskills.io open standard
- OpenClaw Skills - Enhanced agentskills.io packages with runtime requirements, ClawHub registry integration, and automatic dependency checking
All three types coexist seamlessly. Built-in skills expose tools the LLM can call directly. External skills use a metadata-first loading pattern where only name + description enter the system prompt at startup (~24 tokens per skill), and full instructions are loaded on demand via the load_skill tool.
pulsebot/skills/
├── base.py # BaseSkill, ToolDefinition, ToolResult
├── loader.py # SkillLoader (built-in + external discovery)
├── lock.py # LockFile manager for ClawHub-installed skills
├── clawhub_client.py # ClawHub registry REST API client
├── __init__.py
├── agentskills/ # agentskills.io integration
│ ├── __init__.py
│ ├── models.py # SkillMetadata, SkillContent, OpenClawMetadata
│ ├── loader.py # SKILL.md parser, validator, directory scanner
│ └── requirements.py # Runtime requirement checker
└── builtin/ # Built-in skill implementations
├── __init__.py
├── file_ops.py
├── shell.py
└── agentskills_bridge.py # Bridge: load_skill + read_skill_file tools
skills/ # Default directory for external skill packages
└── timeplus-sql-guide/ # Example skill
├── SKILL.md
└── references/
└── syntax-cheatsheet.md
.clawhub/ # ClawHub registry metadata
└── lock.json # Tracks installed skills with content hashes
Startup
│
├─ Load built-in skills (Python classes → tools registered directly)
│ file_ops, shell
│
├─ Discover external skills (scan skill_dirs for SKILL.md files)
│ Parse YAML frontmatter → SkillMetadata (name + description only)
│ Check OpenClaw requirements → Skip if not satisfied
│
└─ If external skills found:
Register AgentSkillsBridge skill → load_skill + read_skill_file tools
Inject skill index into system prompt
| Tier | When | What | Cost |
|---|---|---|---|
| Tier 1 - Metadata | Startup | Name + description in system prompt | ~24 tokens/skill |
| Tier 2 - Full Content | On demand via load_skill |
Full SKILL.md instructions, scripts, references | ~2,000-5,000 tokens |
This means 25 external skills add only ~630 tokens to the base prompt, instead of 50,000-125,000 tokens if all instructions were loaded upfront.
User Message → Agent
↓
LLM with Tools
↓
Tool Call Request
↓
SkillLoader.get_skill_for_tool()
↓
Skill.execute()
↓
ToolResult → LLM → Final Response
skills:
# Built-in Python skills to load
builtin:
- file_ops
- shell
- workspace
# Directories to scan for agentskills.io skill packages
skill_dirs:
- "./skills"
- "/shared/team-skills"
# Skill names to skip during discovery
disabled_skills: []
# ClawHub registry settings
clawhub:
# Directory to install ClawHub skills (defaults to first skill_dirs entry)
install_dir: "./skills"
# Auto-update installed skills on startup
auto_update: false
# Auth token — supports env var substitution like other API keys (preferred)
auth_token: "${CLAWHUB_AUTH_TOKEN:-}"
# Alternative: read token from a file instead of config
# auth_token_path: "~/.clawhub/token"| Field | Type | Default | Description |
|---|---|---|---|
skills.builtin |
list[str] | ["file_ops", "shell", "workspace"] |
Built-in skill names to load |
skills.skill_dirs |
list[str] | [] |
Directories to scan for agentskills.io packages |
skills.disabled_skills |
list[str] | [] |
Skill names to skip (by name field in SKILL.md) |
clawhub.install_dir |
str | First skill_dirs entry |
Default directory for pulsebot skill install |
clawhub.auto_update |
bool | false |
Auto-update installed skills on startup |
clawhub.auth_token |
str | "" |
ClawHub auth token — supports ${CLAWHUB_AUTH_TOKEN:-} env var substitution (preferred) |
clawhub.auth_token_path |
str | None |
Alternative: path to file containing ClawHub auth token (used if auth_token is empty) |
| Action | Configuration |
|---|---|
| Enable external skills | Add directories to skill_dirs |
| Disable all external skills | Set skill_dirs: [] or omit it |
| Disable specific skills | Add names to disabled_skills |
| Add more skill sources | Add more paths to skill_dirs |
| Auto-update ClawHub skills | Set clawhub.auto_update: true |
When running with Docker, mount the skills directory into the container:
# docker-compose.yaml
pulsebot-agent:
volumes:
- ./config.yaml:/app/config.yaml:ro
- ./skills:/app/skills:roRead, write, and list files with security guardrails.
Tools:
read_file- Read file contents (parameter:path)write_file- Write content to file (parameters:path,content,append)list_directory- List directory contents (parameter:path)
Security: Path traversal prevention ensures operations stay within the configured base path.
Execute shell commands with safety controls.
Tool: run_command
- Parameters:
command(string) - Returns: Exit code, stdout, stderr
Safety Features: Blocked commands list (rm, sudo, etc.), dangerous pattern detection, timeout protection (30s default), output size limits.
Manages isolated workspace directories for sessions and tasks.
Tools:
create_workspace- Create a new workspacelist_workspaces- List available workspacesget_workspace_files- List files in a workspaceread_workspace_file- Read a file from a workspace
The agentskills.io standard is a filesystem-based format for packaging AI agent skills, adopted by Claude, GitHub Copilot, OpenAI Codex, Cursor, and others.
A skill is a directory containing a SKILL.md file with YAML frontmatter for metadata and Markdown body for instructions:
my-skill/
├── SKILL.md # Required: metadata + instructions
├── scripts/ # Optional: executable code
├── references/ # Optional: supplementary docs
└── assets/ # Optional: templates, data files
---
name: my-skill
description: What this skill does and when to use it.
license: Apache-2.0
metadata:
author: your-name
version: "1.0"
compatibility: Requires X to be configured
allowed-tools: tool1 tool2
---
# My Skill
Full instructions loaded on demand by the agent.
## When to use this skill
Use when the user asks about X.
## Procedures
Step-by-step instructions...| Field | Required | Description |
|---|---|---|
name |
Yes | 1-64 chars, lowercase letters, digits, hyphens. Must match directory name. |
description |
Yes | 1-1024 chars. Describes capability and trigger conditions. |
license |
No | License identifier (e.g., Apache-2.0) |
compatibility |
No | Environment requirements |
metadata |
No | Arbitrary key-value pairs (author, version, etc.) |
allowed-tools |
No | Space-delimited pre-approved tool list |
- At startup, PulseBot scans
skill_dirsfor directories containingSKILL.md - Only name + description are parsed and injected into the system prompt as a skill index
- When the LLM determines a skill is relevant, it calls
load_skillwith the skill name - The full SKILL.md body (instructions, procedures, examples) is returned to the LLM
- The LLM can read individual files from the skill package via
read_skill_file
The AgentSkillsBridge skill provides two tools for the LLM to interact with external skills:
| Tool | Description |
|---|---|
load_skill |
Load full instructions for a skill by name. Parameters: skill_name (string) |
read_skill_file |
Read a file from a skill's scripts/ or references/ directory. Parameters: skill_name (string), file_path (string) |
These tools are only registered when external skills are discovered. If skill_dirs is empty, they don't appear.
- Create a directory under your skills path:
mkdir -p skills/my-skill/references- Create
SKILL.mdwith frontmatter and instructions:
---
name: my-skill
description: Guide for doing X when the user asks about Y.
---
# My Skill
## When to use
Use this skill when...
## Instructions
1. Do this
2. Then that- Optionally add reference files or scripts:
echo "# Cheatsheet" > skills/my-skill/references/cheatsheet.md- Restart PulseBot - the skill is automatically discovered.
OpenClaw extends the agentskills.io standard with runtime requirements and ClawHub registry integration. Skills can declare what they need (binaries, environment variables, OS) and PulseBot will only load them if requirements are satisfied.
Add an openclaw (or clawdbot, clawdis, moltbot) block to your SKILL.md frontmatter:
---
name: my-advanced-skill
description: A skill that requires specific tools and environment.
metadata:
openclaw:
# Requirements that must ALL be satisfied
requires:
# Required environment variables
env:
- MY_API_KEY
- DATABASE_URL
# Required binaries (all must exist in PATH)
bins:
- docker
- kubectl
# At least ONE of these must exist
anyBins:
- jq
- python3
# Required config files
configs:
- ~/.myapp/config.yaml
# Primary env var shown in error messages
primaryEnv: MY_API_KEY
# Bypass requirement checks (use with caution)
always: false
# Emoji for display
emoji: 🔧
# Supported operating systems
os:
- darwin
- linux
# ClawHub skill key for updates
skillKey: my-team/my-advanced-skill
---| Field | Type | Description |
|---|---|---|
openclaw.requires.env |
list[str] | Required environment variables |
openclaw.requires.bins |
list[str] | Required binaries (all must exist) |
openclaw.requires.anyBins |
list[str] | Alternative binaries (at least one must exist) |
openclaw.requires.configs |
list[str] | Required configuration files |
openclaw.primaryEnv |
str | Primary env var to show in error messages |
openclaw.always |
bool | If true, bypass all requirement checks |
openclaw.emoji |
str | Emoji for display in skill listings |
openclaw.homepage |
str | URL to skill documentation |
openclaw.os |
list[str] | Supported OS: darwin, linux, win32 |
openclaw.skillKey |
str | ClawHub registry key for auto-updates |
The following top-level keys are all aliases for the OpenClaw metadata block:
openclaw(preferred)clawdbotclawdismoltbot
Example with alias:
---
name: my-skill
metadata:
clawdbot:
requires:
env: [API_KEY]
bins: [curl]
---When PulseBot discovers an external skill:
- Parse SKILL.md frontmatter
- If
openclawmetadata exists:- Check OS compatibility
- Verify all required binaries exist in PATH
- Check that at least one of
anyBinsexists - Verify all required environment variables are set
- Check that required config files exist
- If
always: true, skip all checks - If any check fails, the skill is skipped with a warning logged
- If no
openclawmetadata, the skill loads normally (backward compatible)
Binary lookups are cached per startup for performance.
ClawHub is a public registry for OpenClaw-compatible skills. Install skills directly from the registry with automatic dependency checking.
# Search for skills in ClawHub
pulsebot skill search <query>
# Install a skill from ClawHub
pulsebot skill install <slug> [--version VERSION] [--dir DIR]
# List installed ClawHub skills
pulsebot skill list [--dir WORKDIR]
# Remove an installed skill
pulsebot skill remove <slug> [--dir DIR] [--workdir WORKDIR]To install skills from ClawHub, you need to set the CLAWHUB_AUTH_TOKEN environment variable:
# Set the authentication token
export CLAWHUB_AUTH_TOKEN=your_token_here
# Or use a .env file
CLAWHUB_AUTH_TOKEN=your_token_hereThe token is used to authenticate with the ClawHub registry when searching and installing skills. Without a valid token, skill installation will fail.
# Install latest version
pulsebot skill install timeplus/sql-guide
# Install specific version
pulsebot skill install timeplus/sql-guide --version 1.2.0
# Install to custom directory
pulsebot skill install timeplus/sql-guide --dir /shared/skillsWhen you install a skill:
- Downloads the skill ZIP from ClawHub registry
- Validates only allowed text file types are present
- Verifies SHA256 checksum matches the registry
- Installs atomically (all-or-nothing)
- Records the installation in
.clawhub/lock.json - Scans the new skill directory and loads the skill
The lock file tracks ClawHub-installed skills with content hashes for integrity:
{
"version": 1,
"skills": {
"timeplus/sql-guide": {
"slug": "timeplus/sql-guide",
"version": "1.2.0",
"content_hash": "sha256:abc123...",
"installed_at": "2024-01-15T09:30:00Z",
"source": "clawhub"
}
}
}The lock file enables:
- Integrity verification: Detect if skill files were modified
- Auto-updates: Compare installed version with registry latest
- Team synchronization: Share
lock.jsonto ensure consistent installations
Enable automatic skill updates on startup:
clawhub:
auto_update: trueWhen enabled:
- On startup, check all locked skills against ClawHub registry
- If newer version available, download and install
- Only updates if content hash differs (prevents unnecessary reinstalls)
- Logs updates to console
For security, ClawHub only allows text file types. Binary files (images, executables, etc.) are rejected during installation:
Allowed extensions: .md, .txt, .yaml, .yml, .json, .toml, .js, .mjs, .cjs, .ts, .jsx, .tsx, .py, .sh, .bash, .css, .html, .svg, .xml, .csv, .ini, .cfg, .conf, .env, .gitignore, .editorconfig, .rs, .go, .java, .c, .cpp, .h, .hpp, .rb, .php, .swift, .kt, .scala, .sql, .r, .R, .jl, .lua, .pl, .pm
For capabilities that require code execution (API calls, database queries), create a Python skill class.
from pulsebot.skills.base import BaseSkill, ToolDefinition, ToolResult
class MySkill(BaseSkill):
name = "my_skill"
description = "Does something useful"
def __init__(self, api_key: str = ""):
self.api_key = api_key
def get_tools(self) -> list[ToolDefinition]:
return [ToolDefinition(
name="my_tool",
description="Clear description for the LLM",
parameters={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "What to look up"
}
},
"required": ["query"]
}
)]
async def execute(self, tool_name: str, arguments: dict) -> ToolResult:
if tool_name == "my_tool":
# Your implementation here
return ToolResult.ok("Result data")
return ToolResult.fail(f"Unknown tool: {tool_name}")Add to config.yaml:
skills:
custom:
- my_project.skills.my_skill.MySkill- Input Validation: Always validate arguments before processing
- Error Handling: Return descriptive errors via
ToolResult.fail() - Security: Validate paths, commands, and external inputs
- Timeouts: Use
asyncio.wait_for()for external API calls - Naming: Use descriptive, action-oriented tool names (
get_weather, notweather) - Descriptions: Write clear descriptions - this is what the LLM uses to decide when to call your tool
| Use Case | Approach |
|---|---|
| Need to call APIs, run code, access databases | Custom Python skill |
| Procedural knowledge, guides, SQL templates | External agentskills.io skill |
| Skills with runtime dependencies (binaries, env vars) | OpenClaw-compatible skill |
| Share skills via registry | ClawHub-published skill |
| Team-shared domain knowledge | External skill in a shared skill_dirs path |
| Portable across AI platforms | External skill (agentskills.io standard) |
| Core agent capability | Built-in skill |
External skill not discovered:
- Verify the directory contains a
SKILL.mdfile - Check that
namein frontmatter matches the directory name exactly - Ensure the directory is inside a path listed in
skill_dirs - Check that the skill name is not in
disabled_skills - Look for validation warnings in logs
- For OpenClaw skills, check requirement checker warnings
load_skill / read_skill_file tools not available:
- These only appear when at least one external skill is discovered
- Verify
skill_dirsis configured and contains valid skill packages
"Unknown built-in skill" error:
- Check that the skill name matches one of:
file_ops,shell,workspace
Tool not being called by LLM:
- Check tool description is clear and specific
- Ensure parameter schema is correct JSON Schema
- Verify tool name is unique across all skills
ClawHub install fails:
- Check network connectivity to
https://clawhub.ai - Verify the skill slug exists in the registry
- Ensure
--dirpoints to a writable directory - Check for disallowed file type errors (only text files allowed)
Skill requirements not satisfied:
- Check logs for specific missing binaries or env vars
- Verify required binaries are in PATH
- Set missing environment variables
- Use
always: truein OpenClaw metadata to bypass checks (with caution)