Skill Development
Skills are pluggable tool bundles that extend agent capabilities in LibreFang. A skill packages one or more tools with their implementation, letting agents do things that built-in tools do not cover. This guide covers skill creation, the manifest format, Python and WASM runtimes, publishing to FangHub, and CLI management.
Table of Contents
- Overview
- Skill Format
- Python Skills
- WASM Skills
- Skill Requirements
- Installing Skills
- Publishing to FangHub
- CLI Commands
- OpenClaw Compatibility
- Best Practices
Overview
A skill consists of:
- A manifest (
skill.tomlorSKILL.md) that declares metadata, runtime type, provided tools, and requirements. - An entry point (Python script, WASM module, Node.js module, or prompt-only Markdown) that implements the tool logic.
Skills are installed to ~/.librefang/skills/ and made available to agents through the skill registry. LibreFang ships with 60 bundled skills that are compiled into the binary and available immediately.
Supported Runtimes
| Runtime | Language | Sandboxed | Notes |
|---|---|---|---|
python | Python 3.8+ | No (subprocess with env_clear()) | Easiest to write. Uses stdin/stdout JSON protocol. |
wasm | Rust, C, Go, etc. | Yes (Wasmtime dual metering) | Fully sandboxed. Best for security-sensitive tools. |
node | JavaScript/TypeScript | No (subprocess) | OpenClaw compatibility. |
prompt_only | Markdown | N/A | Expert knowledge injected into system prompt. No code execution. |
builtin | Rust | N/A | Compiled into the binary. For core tools only. |
60 Bundled Skills
LibreFang includes 60 expert knowledge skills compiled into the binary (no installation needed):
| Category | Skills |
|---|---|
| DevOps & Infra | ci-cd, ansible, prometheus, nginx, kubernetes, terraform, helm, docker, sysadmin, shell-scripting, linux-networking |
| Cloud | aws, gcp, azure |
| Languages | rust-expert, python-expert, typescript-expert, golang-expert |
| Frontend | react-expert, nextjs-expert, css-expert |
| Databases | postgres-expert, redis-expert, sqlite-expert, mongodb, elasticsearch, sql-analyst |
| APIs & Web | graphql-expert, openapi-expert, api-tester, oauth-expert |
| AI/ML | ml-engineer, llm-finetuning, vector-db, prompt-engineer |
| Security | security-audit, crypto-expert, compliance |
| Dev Tools | github, git-expert, jira, linear-tools, sentry, code-reviewer, regex-expert |
| Writing | technical-writer, writing-coach, email-writer, presentation |
| Data | data-analyst, data-pipeline |
| Collaboration | slack-tools, notion, confluence, figma-expert |
| Career | interview-prep, project-manager |
| Advanced | wasm-expert, pdf-reader, web-search |
These are prompt_only skills using the SKILL.md format -- expert knowledge that gets injected into the agent's system prompt.
SKILL.md Format
The SKILL.md format (also used by OpenClaw) uses YAML frontmatter and a Markdown body:
---
name: rust-expert
description: Expert Rust programming knowledge
---
# Rust Expert
## Key Principles
- Ownership and borrowing rules...
- Lifetime annotations...
## Common Patterns
...
SKILL.md files are automatically parsed and converted to prompt_only skills. All SKILL.md files pass through an automated prompt injection scanner that detects override attempts, data exfiltration patterns, and shell references before inclusion.
Skill Format
Directory Structure
my-skill/
skill.toml # Manifest (required)
src/
main.py # Entry point (for Python skills)
README.md # Optional documentation
Manifest (skill.toml)
[skill]
name = "web-summarizer"
version = "0.1.0"
description = "Summarizes any web page into bullet points"
author = "librefang-community"
license = "MIT"
tags = ["web", "summarizer", "research"]
[runtime]
type = "python"
entry = "src/main.py"
[[tools.provided]]
name = "summarize_url"
description = "Fetch a URL and return a concise bullet-point summary"
input_schema = { type = "object", properties = { url = { type = "string", description = "The URL to summarize" } }, required = ["url"] }
[[tools.provided]]
name = "extract_links"
description = "Extract all links from a web page"
input_schema = { type = "object", properties = { url = { type = "string" } }, required = ["url"] }
[requirements]
tools = ["web_fetch"]
capabilities = ["NetConnect(*)"]
Manifest Sections
[skill] -- Metadata
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique skill name (used as install directory name) |
version | string | No | Semantic version (default: "0.1.0") |
description | string | No | Human-readable description |
author | string | No | Author name or organization |
license | string | No | License identifier (e.g., "MIT", "Apache-2.0") |
tags | array | No | Tags for discovery on FangHub |
[runtime] -- Execution Configuration
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | "python", "wasm", "node", or "builtin" |
entry | string | Yes | Relative path to the entry point file |
[[tools.provided]] -- Tool Definitions
Each [[tools.provided]] entry defines one tool that the skill provides:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Tool name (must be unique across all tools) |
description | string | Yes | Description shown to the LLM |
input_schema | object | Yes | JSON Schema defining the tool's input parameters |
[requirements] -- Host Requirements
| Field | Type | Description |
|---|---|---|
tools | array | Built-in tools this skill needs the host to provide |
capabilities | array | Capability strings the agent must have |
Python Skills
Python skills are the simplest to write. They run as subprocesses and communicate via JSON over stdin/stdout.
Protocol
- LibreFang sends a JSON payload to the script's stdin:
{
"tool": "summarize_url",
"input": {
"url": "https://example.com"
},
"agent_id": "uuid-...",
"agent_name": "researcher"
}
- The script processes the input and writes a JSON result to stdout:
{
"result": "- Point one\n- Point two\n- Point three"
}
If an error occurs, return an error object:
{
"error": "Failed to fetch URL: connection refused"
}
Example: Web Summarizer
src/main.py:
#!/usr/bin/env python3
"""LibreFang skill: web-summarizer"""
import json
import sys
import urllib.request
def summarize_url(url: str) -> str:
"""Fetch a URL and return a basic summary."""
req = urllib.request.Request(url, headers={"User-Agent": "LibreFang-Skill/1.0"})
with urllib.request.urlopen(req, timeout=30) as resp:
content = resp.read().decode("utf-8", errors="replace")
# Simple extraction: first 500 chars as summary
text = content[:500].strip()
return f"Summary of {url}:\n{text}..."
def extract_links(url: str) -> str:
"""Extract all links from a web page."""
import re
req = urllib.request.Request(url, headers={"User-Agent": "LibreFang-Skill/1.0"})
with urllib.request.urlopen(req, timeout=30) as resp:
content = resp.read().decode("utf-8", errors="replace")
links = re.findall(r'href="(https?://[^"]+)"', content)
unique_links = list(dict.fromkeys(links))
return "\n".join(unique_links[:50])
def main():
payload = json.loads(sys.stdin.read())
tool_name = payload["tool"]
input_data = payload["input"]
try:
if tool_name == "summarize_url":
result = summarize_url(input_data["url"])
elif tool_name == "extract_links":
result = extract_links(input_data["url"])
else:
print(json.dumps({"error": f"Unknown tool: {tool_name}"}))
return
print(json.dumps({"result": result}))
except Exception as e:
print(json.dumps({"error": str(e)}))
if __name__ == "__main__":
main()
Using the LibreFang Python SDK
For more advanced skills, use the Python SDK (sdk/python/librefang_sdk.py):
#!/usr/bin/env python3
from librefang_sdk import SkillHandler
handler = SkillHandler()
@handler.tool("summarize_url")
def summarize_url(url: str) -> str:
# Your implementation here
return "Summary..."
@handler.tool("extract_links")
def extract_links(url: str) -> str:
# Your implementation here
return "link1\nlink2"
if __name__ == "__main__":
handler.run()
WASM Skills
WASM skills run inside a sandboxed Wasmtime environment. They are ideal for security-sensitive operations because the sandbox enforces resource limits and capability restrictions.
Building a WASM Skill
- Write your skill in Rust (or any language that compiles to WASM):
// src/lib.rs
use std::io::{self, Read};
#[no_mangle]
pub extern "C" fn _start() {
let mut input = String::new();
io::stdin().read_to_string(&mut input).unwrap();
let payload: serde_json::Value = serde_json::from_str(&input).unwrap();
let tool = payload["tool"].as_str().unwrap_or("");
let input_data = &payload["input"];
let result = match tool {
"my_tool" => {
let param = input_data["param"].as_str().unwrap_or("");
format!("Processed: {param}")
}
_ => format!("Unknown tool: {tool}"),
};
println!("{}", serde_json::json!({"result": result}));
}
- Compile to WASM:
cargo build --target wasm32-wasi --release
- Reference the
.wasmfile in your manifest:
[runtime]
type = "wasm"
entry = "target/wasm32-wasi/release/my_skill.wasm"
Sandbox Limits
The WASM sandbox enforces:
- Fuel limit: Maximum computation steps (prevents infinite loops).
- Memory limit: Maximum memory allocation.
- Capabilities: Only the capabilities granted to the agent apply.
These are derived from the agent's [resources] section in its manifest.
Skill Requirements
Skills can declare requirements in the [requirements] section:
Tool Requirements
If your skill needs to call built-in tools (e.g., web_fetch to download a page before processing it):
[requirements]
tools = ["web_fetch", "file_read"]
The skill registry validates that the agent has these tools available before loading the skill.
Capability Requirements
If your skill needs specific capabilities:
[requirements]
capabilities = ["NetConnect(*)", "ShellExec(python3)"]
Installing Skills
From a Local Directory
librefang skill install /path/to/my-skill
This reads the skill.toml, validates the manifest, and copies the skill to ~/.librefang/skills/my-skill/.
From FangHub
librefang skill install web-summarizer
This downloads the skill from the FangHub marketplace registry.
From a Git Repository
librefang skill install https://github.com/user/librefang-skill-example.git
Listing Installed Skills
librefang skill list
Output:
3 skill(s) installed:
NAME VERSION TOOLS DESCRIPTION
----------------------------------------------------------------------
web-summarizer 0.1.0 2 Summarizes any web page into bullet points
data-analyzer 0.2.1 3 Statistical analysis tools
code-formatter 1.0.0 1 Format code in 20+ languages
Removing Skills
librefang skill remove web-summarizer
Publishing to FangHub
FangHub is the community skill marketplace for LibreFang.
Preparing Your Skill
- Ensure your
skill.tomlhas complete metadata:name,version,description,author,license,tags
- Include a
README.mdwith usage instructions. - Test your skill locally:
librefang skill install /path/to/my-skill
# Spawn an agent with the skill's tools and test them
Searching FangHub
librefang skill search "web scraping"
Output:
Skills matching "web scraping":
web-summarizer (42 stars)
Summarizes any web page into bullet points
https://fanghub.dev/skills/web-summarizer
page-scraper (28 stars)
Extract structured data from web pages
https://fanghub.dev/skills/page-scraper
Publishing
Publishing to FangHub will be available via:
librefang skill publish
This validates the manifest, packages the skill, and uploads it to the FangHub registry.
CLI Commands
Full Skill Command Reference
# Install a skill (local directory, FangHub name, or git URL)
librefang skill install <source>
# List all installed skills
librefang skill list
# Remove an installed skill
librefang skill remove <name>
# Search FangHub for skills
librefang skill search <query>
# Create a new skill scaffold (interactive)
librefang skill create
Creating a Skill Scaffold
librefang skill create
This interactive command prompts for:
- Skill name
- Description
- Runtime type (python/node/wasm)
It generates:
~/.librefang/skills/my-skill/
skill.toml # Pre-filled manifest
src/
main.py # Starter entry point (for Python)
The generated entry point includes a working template that reads JSON from stdin and writes JSON to stdout.
Using Skills in Agent Manifests
Reference skills in the agent manifest's skills field:
name = "my-assistant"
version = "0.1.0"
description = "An assistant with extra skills"
author = "librefang"
module = "builtin:chat"
skills = ["web-summarizer", "data-analyzer"]
[model]
provider = "groq"
model = "llama-3.3-70b-versatile"
[capabilities]
tools = ["file_read", "web_fetch", "summarize_url"]
memory_read = ["*"]
memory_write = ["self.*"]
The kernel loads skill tools and prompts at agent spawn time, merging them with the agent's base capabilities.
OpenClaw Compatibility
LibreFang can install and run OpenClaw-format skills. The skill installer auto-detects OpenClaw skills (by looking for package.json + index.ts/index.js) and converts them.
Automatic Conversion
librefang skill install /path/to/openclaw-skill
If the directory contains an OpenClaw-style skill (Node.js package), LibreFang:
- Detects the OpenClaw format.
- Generates a
skill.tomlmanifest frompackage.json. - Maps tool names to LibreFang conventions.
- Copies the skill to the LibreFang skills directory.
Manual Conversion
If automatic conversion does not work, create a skill.toml manually:
[skill]
name = "my-openclaw-skill"
version = "1.0.0"
description = "Converted from OpenClaw"
[runtime]
type = "node"
entry = "index.js"
[[tools.provided]]
name = "my_tool"
description = "Tool description"
input_schema = { type = "object", properties = { input = { type = "string" } }, required = ["input"] }
Place this alongside the existing index.js/index.ts and install:
librefang skill install /path/to/skill-directory
Skills imported via librefang migrate --from openclaw are also scanned and reported in the migration report, with instructions for manual reinstallation.
Best Practices
- Keep skills focused -- one skill should do one thing well.
- Declare minimal requirements -- only request the tools and capabilities your skill actually needs.
- Use descriptive tool names -- the LLM reads the tool name and description to decide when to use it.
- Provide clear input schemas -- include descriptions for every parameter so the LLM knows what to pass.
- Handle errors gracefully -- always return a JSON error object rather than crashing.
- Version carefully -- use semantic versioning; breaking changes require a major version bump.
- Test with multiple agents -- verify your skill works with different agent templates and providers.
- Include a README -- document setup steps, dependencies, and example usage.