Install
openclaw skills install openclaw-plusMulti-capability dev skill for chained workflows involving Python execution, package management, git, HTTP requests, file operations, process management, sub-agents, or webhook notifications. Use when the task combines two or more of these — or when any single one requires disciplined error handling.
openclaw skills install openclaw-plusA modular super-skill for development and automation workflows. Chain capabilities together or use them individually.
| Capability | Use for |
|---|---|
run_python | Execute Python code or scripts |
install_package | Install pip/system packages |
git_status / git_commit | Version control operations |
http_request | Fetch URLs or call APIs (GET, POST, PUT, DELETE) |
file_ops | Read, write, move, copy, delete, diff files |
process | Start, monitor, and stop background processes |
sub_agent | Delegate tasks to a spawned agent |
notify | Send webhook notifications (Slack, Discord, ntfy, custom) |
run_python)# Simple execution
run_python("print('Hello, world!')")
# Multi-line with packages
run_python("""
import pandas as pd
df = pd.read_csv('data.csv')
print(df.describe())
""")
Notes:
.py file first then executeinstall_package)install_package("pandas") # single
install_package("numpy==1.24.0") # pinned version
install_package("requests beautifulsoup4") # multiple
install_package("-r requirements.txt") # from file
Implementation: pip install <package> --break-system-packages
Always check if a package is already installed before installing. Handle version conflicts explicitly.
git_status, git_commit)git_status() # check current state
git_status("/path/to/repo") # specific directory
git_commit("feat: add data pipeline") # commit staged files
git_commit("fix: resolve parsing error", stage_all=True) # stage all + commit
Conventional commit types: feat, fix, docs, style, refactor, test, chore
Keep first line under 50 characters. Always run git_status() before committing. Configure user.email and user.name if git commit fails with identity errors.
http_request)Unified capability for both URL fetching and API calls.
# GET request
data = http_request("https://api.example.com/users")
# POST with JSON body
result = http_request(
"https://api.example.com/create",
method="POST",
json={"name": "Alice", "role": "admin"}
)
# With authentication
data = http_request(
"https://api.example.com/protected",
headers={"Authorization": "Bearer TOKEN"}
)
# GraphQL
result = http_request(
"https://api.example.com/graphql",
method="POST",
json={"query": "{ users { id name } }"}
)
Notes:
response.raise_for_status() before parsingfile_ops)Read, write, move, copy, delete, and diff files without shelling out manually.
# Read
content = file_ops("read", "data/report.txt")
data = file_ops("read", "config.json", parse_json=True)
# Write
file_ops("write", "output/results.txt", content="Analysis complete.")
file_ops("write", "data/config.json", content={"key": "value"}, as_json=True)
# Append
file_ops("append", "logs/run.log", content="[2026-05-28] Step complete\n")
# Copy / Move
file_ops("copy", "report.txt", dest="archive/report_v1.txt")
file_ops("move", "temp_output.csv", dest="final/output.csv")
# Delete
file_ops("delete", "temp/scratch.txt")
file_ops("delete", "temp/", recursive=True) # delete directory
# Diff
diff = file_ops("diff", "old_config.json", "new_config.json")
# List directory
entries = file_ops("list", "data/", pattern="*.csv")
Best practices:
append over write for log files to avoid overwritingprocess)Start background processes, check their status, tail output, and stop them.
# Start a background process
pid = process("start", "python3 server.py", cwd="/app", capture_output=True)
# Start and get a handle
job = process("start", "npm run build", env={"NODE_ENV": "production"})
# Check status
status = process("status", pid)
# Returns: { "pid": 1234, "running": True, "exit_code": None }
# Tail output
logs = process("logs", pid, lines=50)
# Wait for completion (blocks)
result = process("wait", pid, timeout=120)
# Returns: { "exit_code": 0, "stdout": "...", "stderr": "..." }
# Kill
process("kill", pid)
process("kill", pid, signal="SIGTERM") # graceful shutdown
Common patterns:
# Start a server, wait for it to be ready, then run tests
server_pid = process("start", "./start_server.sh")
time.sleep(2) # brief startup wait
result = process("wait", test_pid, timeout=60)
if result["exit_code"] != 0:
logs = process("logs", test_pid)
print(f"Tests failed:\n{logs}")
finally:
process("kill", server_pid)
Notes:
process("wait", pid, timeout=N) rather than time.sleep for predictable completionexit_code after wait — a 0 is success, anything else needs handlingsub_agent)Delegate a task to a spawned agent and get the result back. Use when a task is large enough to benefit from parallel work or clean separation of concerns.
# Spawn an agent with a specific task
result = sub_agent(
task="Analyze the CSV at data/sales.csv and return a JSON summary with total revenue, top 5 products, and monthly breakdown.",
context={"file": "data/sales.csv"}
)
# Spawn multiple agents in parallel
results = sub_agent(
tasks=[
"Summarize the Q1 report at reports/q1.pdf",
"Summarize the Q2 report at reports/q2.pdf",
"Summarize the Q3 report at reports/q3.pdf"
],
parallel=True
)
# Agent with specific tools/skills
result = sub_agent(
task="Generate a bar chart of monthly revenue from data/revenue.json and save it as charts/revenue.png",
skills=["run_python", "file_ops"]
)
When to use sub-agents:
When NOT to use sub-agents:
Notes:
notify)Send notifications when a workflow step completes, fails, or needs attention.
# Slack
notify(
"slack",
webhook_url=os.environ["SLACK_WEBHOOK"],
message="✅ Data pipeline complete. 4,821 records processed.",
channel="#data-alerts"
)
# Discord
notify(
"discord",
webhook_url=os.environ["DISCORD_WEBHOOK"],
message="Build failed on main branch.",
username="OpenClaw Bot",
embed={
"title": "Build Failure",
"description": "Step 3 exited with code 1",
"color": 0xFF0000
}
)
# ntfy (self-hosted push notifications)
notify(
"ntfy",
topic="my-agent-alerts",
message="Scraping job finished: 320 products saved.",
priority="default",
tags=["white_check_mark"]
)
# Generic webhook (any HTTP endpoint)
notify(
"webhook",
url="https://hooks.example.com/alert",
payload={"event": "pipeline_complete", "records": 4821, "status": "ok"},
headers={"X-Secret": os.environ["WEBHOOK_SECRET"]}
)
Best practices:
notify at the end of long workflows so the user doesn't have to watchFailure notification pattern:
try:
run_pipeline()
notify("slack", webhook_url=SLACK_URL, message="✅ Pipeline complete")
except Exception as e:
notify("slack", webhook_url=SLACK_URL, message=f"❌ Pipeline failed: {e}")
raise
install_package("pandas requests")
data = http_request("https://api.example.com/dataset")
file_ops("write", "raw_data.json", content=data, as_json=True)
run_python("""
import pandas as pd, json
df = pd.read_json('raw_data.json')
df.dropna().to_csv('clean_data.csv', index=False)
print(f"Processed {len(df)} records")
""")
git_commit("feat: add cleaned dataset", stage_all=True)
notify("slack", webhook_url=os.environ["SLACK_WEBHOOK"], message="✅ Pipeline done")
# Delegate each document to a sub-agent in parallel
reports = ["q1.pdf", "q2.pdf", "q3.pdf", "q4.pdf"]
summaries = sub_agent(
tasks=[f"Summarize reports/{r} and return JSON with key_findings and total_revenue" for r in reports],
parallel=True
)
file_ops("write", "output/annual_summary.json", content=summaries, as_json=True)
notify("slack", webhook_url=os.environ["SLACK_WEBHOOK"], message="Annual summaries ready")
server = process("start", "python3 -m app.server", cwd="/app")
time.sleep(2)
try:
result = process("wait",
process("start", "pytest tests/integration/", capture_output=True),
timeout=120
)
status = "✅ All tests passed" if result["exit_code"] == 0 else "❌ Tests failed"
finally:
process("kill", server)
notify("slack", webhook_url=os.environ["SLACK_WEBHOOK"], message=status)
csv_files = file_ops("list", "data/incoming/", pattern="*.csv")
results = sub_agent(
tasks=[f"Validate and clean data/incoming/{f}, save to data/processed/{f}" for f in csv_files],
parallel=True,
skills=["file_ops", "run_python"]
)
file_ops("write", "data/processing_report.json", content=results, as_json=True)
git_commit("chore: process incoming CSV batch", stage_all=True)
Always handle errors at each step — don't let a mid-workflow failure leave things in a broken state.
# File operations
if not file_ops("exists", "data/input.csv"):
raise FileNotFoundError("data/input.csv not found — aborting")
# Process management
result = process("wait", pid, timeout=60)
if result["exit_code"] != 0:
logs = process("logs", pid)
notify("slack", webhook_url=SLACK_URL, message=f"❌ Process failed:\n{logs[-500:]}")
raise RuntimeError(f"Process exited {result['exit_code']}")
# Sub-agent results
for i, result in enumerate(sub_agent_results):
if result.get("error"):
print(f"Sub-agent {i} failed: {result['error']}")
# HTTP requests
response = http_request(url)
if response.status_code != 200:
raise RuntimeError(f"API returned {response.status_code}: {response.text}")
# Git — nothing to commit
status = git_status()
if "nothing to commit" not in status:
git_commit("chore: update output files", stage_all=True)
process("start", ...).run_python(code)
install_package("name" or "-r requirements.txt")
git_status() / git_commit("message", stage_all=True)
http_request(url, method="GET", json={}, headers={})
file_ops("read|write|append|copy|move|delete|diff|list|exists", path, ...)
process("start|status|logs|wait|kill", pid_or_cmd, ...)
sub_agent(task="...", parallel=False, skills=[...])
notify("slack|discord|ntfy|webhook", webhook_url="...", message="...")