Give a man a fish, and you feed him for a day; teach a man to fish, and you feed him for a lifetime.

An autonomous agent is only as capable as the tools it can reach. In Parts 1 and 2 we built the body of our OpenClaw agent — the Gateway, the workspace files, memory, and automation. In this third installment, we give it hands: skills.
A Skill in OpenClaw is a versioned bundle of files — at minimum a SKILL.md, plus any supporting scripts, references, or assets — that teaches the agent how to perform a specific task. Skills are not plugins and they do not replace your core workspace files (AGENTS.md, SOUL.md, TOOLS.md); they are supplemental domain guidance loaded alongside them. The beauty of the model is that a skill is just instructions plus optional code — portable, auditable, and entirely under your control.
This walkthrough is deliberately practical. We will connect the agent to your Gmail, Calendar, and Drive through Gog, give it a private, on-device memory with QMD, teach it to summarize anything locally with Summarize Pro, transcribe audio without a single cloud call using Whisper.cpp, and search the web without API keys. Finally, we will distill everything learned into a daily-briefing skill you can write yourself — because the best skill is the one built for your own life.
A Skill in OpenClaw is a versioned bundle of files — at minimum a SKILL.md, plus any supporting scripts, references, or assets — that teaches the agent how to perform a specific task. Skills are not plugins and they do not replace your core workspace files (AGENTS.md, SOUL.md, TOOLS.md); they are supplemental domain guidance loaded alongside them.
This walkthrough uses one of the most useful skills available: Gog, a Google Workspace CLI that gives the agent direct, script-friendly access to Gmail, Calendar, Drive, Contacts, Sheets, Docs, and more. The setup has four moving parts:
# Search by plain language
openclaw skills search "calendar"
@steipete/gog Gog Google Workspace CLI for Gmail, Calendar, Drive, Contacts, Sheets, and Docs.
@haidiantoutou/calendar Calendar Calendar management and scheduling. Create events, manage meetings, and sync across calendar providers.
[...]
Use this client for an AI-powered agent, Download the JSON file.
Critical caveat: Testing-mode refresh tokens expire after 7 days. While the app’s publishing status is Testing, Google issues refresh tokens that expire after seven days.
brew install openclaw/tap/gogcli, a single Go CLI for Gmail, Calendar, Drive, Docs, Sheets, Slides, Forms, Apps Script, Contacts, Tasks, and Workspace admin — built for terminals, scripts, CI, and coding agents. # Install the Skill
openclaw skills install @steipete/gog
# Load the OAuth client JSON
gog auth credentials set /path/to/client_secret.json
# You can confirm what is stored with:
gog auth credentials list
CLIENT PATH SECRET_KEYRING DOMAINS
default /Users/nmaximo7/Library/Application Support/gogcli/credentials.json true
# Check which services exist before choosing scopes
gog auth services
# **Authorize the account**
gog auth add your-email@gmail.com --services gmail,calendar,drive
# A more complete alternative
gog auth add your-email@gmail.com \
–services gmail,calendar,drive,contacts,docs,sheets,tasks Google sign-in requires a Google Account or Google Workspace account; if consent fails, retry with explicit –services and preserve existing services when reauthorizing… ```
Verify the authorization.
gog auth list
nmaximo7@gmail.com default calendar,drive,gmail 2026-10-23T13:16:37Z oauth
# Gmail: message-level search is more reliable than thread-only search
gog gmail messages search -a your-email@gmail.com 'in:inbox newer_than:7d' \
--max=5 --json --results-only
# Calendar
gog calendar calendars -a your-email@gmail.com --json --results-only
gog calendar events --all -a your-email@gmail.com --days=2 --max=20 \
--json --results-only
# Sheets (replace with a real sheet ID)
gog sheets metadata YOUR_SHEET_ID -a your-email@gmail.com --json --results-only
Wire the skill to the Gateway. If gog and OpenClaw run under the same user with access to the login keychain, the skill should work as soon as you start a new session. A natural-language test after that: “Check my inbox for anything urgent from the last two days, and summarise it.”
# 1. Install the runtime and dependencies
# macOS: Homebrew SQLite is required for extension support
brew install sqlite
# Verify your Node version
node --version # must be >= 22.0.0
# Install QMD globally (pick one)
npm install -g @tobilu/qmd
# or
bun install -g @tobilu/qmd
# 2. Install the OpenClaw skill
openclaw skills install @tobilu/qmd
# openclaw skills install installs the skill instructions. It does not install the qmd binary.
openclaw skills list
# A collection maps a directory to a searchable unit.
# 3. Then, add your directory as a QMD collection and build its index:
# --name is the collection identifier used in searches and context paths.
# The --mask flag restricts indexing to markdown files; without it, QMD may pick up more than you intend.
qmd collection add /Users/nmaximo7/macOS --name macos --mask "**/*.md"
# Verify the collection was added:
qmd collection list
qmd collection show macos # details: path, glob, context count, update command
qmd collection rename macos mac # rename
qmd collection remove macos # remove collection and its indexed documents
# 4. Add context. This matters for agent workflows: it gives the LLM a short,
# human-authored description of what the collection contains, which improves document selection.
qmd context add qmd://macos "Personal macOS notes, scripts, and personal files"
# Path-specific context for subdirectories
qmd context add qmd://macos/work "Work-related notes"
qmd context add qmd://macos/personal "Personal journal entries"
# Other useful context commands:
qmd context list # list all contexts
qmd context check # find collections/paths missing context
qmd context rm qmd://macos/work # remove a context entry
# 5. Indexing and embeddings
# Index new/changed files across all collections
qmd update
# Generate embeddings (required for vsearch and query)
# It downloads the embedding model on first run and chunks documents into ~900-token pieces
# with smart boundary detection.
qmd embed
# 6. Search within a specific collection
# Search within a specific collection
qmd search "Todo" -c macos
# Retrieve full document content
qmd get "docs/assets/Todo.md" --full
# Semantic search across everything
# vsearch embeds the query and computes cosine distance across document chunks.
qmd vsearch "how to configure the gateway"
qmd vsearch "authentication" # finds login, OAuth, SSO, credentials
# Full hybrid search with query expansion and re-ranking (recommended).
qmd query "backup and restore procedure"
# 7. OpenClaw has a built-in QMD memory backend that manages collections, updates, and embeddings for you.
# Enable it in openclaw.json:
{
memory: {
backend: "qmd"
}
}
# 1. The backend creates collections from the workspace’s memory files (MEMORY.md and the memory/ tree)
# and any memory.qmd.paths you configure.
# 2. It also runs qmd update on a schedule
# 3. The binary still must be installed and on the Gateway’s PATH, and Homebrew SQLite is still required.
Summarize Pro is a 20-feature summarization skill that activates on phrases like “summarize,” “tldr,” “eli5,” “key takeaways,” “action items,” “executive summary,” “meeting notes,” and “summarize in [X] words”. It handles articles, PDFs, meetings, emails, YouTube transcripts, books, and conversations.
This is the key architectural point: Summarize Pro does not call a summarization API. The summarization is performed by the AI model that OpenClaw is already running —Ollama, OpenRouter, or whatever provider you have configured. The skill itself only reads and writes local JSON files under ~/.openclaw/summarize-pro/.
The ClawHub listing is explicit: “All processing happens locally — NO external API calls, NO network requests, NO data sent to any server” and “All summarization is done by the AI model itself — no third-party summarizer”.
This means the only “API” involved is your existing model provider. If you are running Ollama locally, the entire pipeline is offline. However, quality depends entirely on your base model. A 1.5B autocomplete model will produce poor summaries; a 27B chat model will do well.
# Installation
openclaw skills install @mkpareek0315/summarize-pro
# Use
Summarize this: [...]
📝 **SUMMARY**
━━━━━━━━━━━━━━━━━━
• **ClawHub** is the public registry for OpenClaw skills and plugins (clawhub.ai).
• **Skills** are versioned instruction bundles (`SKILL.md` + files); **plugins** and **bundle plugins** are code packages distributed via the registry.
• Native install commands: `openclaw skills install @author/skill`, `openclaw plugins install clawhub:`, plus `update --all`.
• The separate **clawhub CLI** handles registry-authenticated workflows: login, publish, delete/undelete, explore, inspect, install/update skills directly into `./skills` with `.clawhub/lock.json`.
• Publishing supports local folders, Git repos (`owner/repo@ref`), and npm-pack `.tgz` artifacts with digest verification.
• Scan-held or blocked releases can disappear from public catalogs; security scans run on all uploads.
• Publish requires a GitHub account old enough to pass an upload gate; signed-in users can report content; moderators can hide/restore/ban.
• Telemetry: `clawhub install` may send best-effort install events unless `CLAWHUB_DISABLE_TELEMETRY=1`.
• Env overrides cover site URL, registry API, config path, workdir, and telemetry.
📊 Stats: ~1,000 words → ~190 words (~81% reduction)
Search skill that runs entirely on the user's machine. Scrapes DuckDuckGo / Bing / Google HTML directly with automatic engine fallback (DDG -> Bing -> Google). Use whenever the user needs real-time web information, latest news, or updated content. No API key, no SDK, no cloud hop, and returns clean JSON.
# Install it in OpenClaw:
openclaw skills install @sakurakilove/local-search
# Confirm it’s loaded:
openclaw skills list
# Start a new chat/session, or restart the Gateway if your current session doesn’t pick it up. Then try:
Use local-search to find recent OpenClaw most popular skills. Give me five results with titles, short summaries, dates, and links.
Alternatives: OpenClaw has native SearXNG support as a key-free web_search provider: “Free and unlimited — no API key or commercial subscription required”. You run SearXNG locally (Docker is the easiest path), point OpenClaw at it, and every search goes through your own metasearch instance. Claw Search (openclaw skills install @yuanchao193/claw-search-free) provides web, image, news, and autocomplete search through a free API that requires no key, with a 100 requests/minute limit, powered by DuckDuckGo as a fallback.
It lives in ~/.openclaw/agents/main/.
Whisper.cpp is a C/C++ port of OpenAI’s Whisper speech-recognition model.
# Install the binary
brew install whisper-cpp
brew install ffmpeg
❯ which whisper-cli
/opt/homebrew/bin/whisper-cli
# Download a model. Models are GGML files hosted on Hugging Face.
mkdir -p ~/macOS/tools
curl -L -o ~/macOS/tools/ggml-large-v3.bin \
"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3.bin?download=true"
# Set the model path once
export WHISPER_CPP_MODEL="$HOME/macOS/tools/ggml-large-v3.bin"
# To persist it, add the export to ~/.zshrc and to the Gateway service environment.
launchctl setenv WHISPER_CPP_MODEL ~/macOS/tools/ggml-large-v3-turbo.bin
Using whisper-cli directly: whisper-cli -m "$WHISPER_CPP_MODEL" -f ~/Downloads/sample-speech-1m.mp3 -otxt
Create a skill (/Users/nmaximo7/.openclaw/agents/main/agent/workshop-skills/), mkdir local-audio, touch SKILL.md
---
name: local-audio
description: "Transcribe audio or video files locally with whisper-cli (whisper.cpp, no API key). Use for speech, meetings, and voice notes."
metadata: {"openclaw":{"emoji":"🎙️","requires":{"bins":["whisper-cli","ffmpeg"]}}}
---
# Local Audio Transcription
When the user asks to transcribe an audio or video file, use the local
`whisper-cli` binary from whisper.cpp. No API key, no network calls.
## Prerequisites
Both binaries must exist on `PATH`:
- `whisper-cli` (Homebrew package `whisper-cpp`)
- `ffmpeg` (required for decoding MP3, M4A, OGG/Opus, and video containers)
The model path is read from `WHISPER_CPP_MODEL`; if it is unset, default to
`$HOME/macOS/tools/ggml-large-v3-turbo.bin`.
## Transcribe
```bash
MODEL="${WHISPER_CPP_MODEL:-$HOME/macOS/tools/ggml-large-v3-turbo.bin}"
whisper-cli -m "$MODEL" -f "<FILE_PATH>" -l auto -otxt -osrt
This writes <FILE_PATH>.txt and <FILE_PATH>.srt next to the source file.
For a plain transcript only, drop -osrt. For timestamps in JSON, add -ojson.
## Read the transcript
Read the generated .txt file and return it to the user.
Restart your OpenClaw gateway by running openclaw gateway restart in your terminal.
# To check whether OpenClaw sees this new skill and whether it’s available to your agent, run:
openclaw skills check
# If it still doesn’t appear, restart the Gateway and check again:
openclaw gateway restart
# You can now prompt your agent directly in your chat interface:
"Transcribe the file at ~/Downloads/sample-speech.mp3".
To force local transcription and prevent fallback to a cloud provider, use the models array with a CLI entry:
{
"tools": {
"web": {
"search": {
"provider": "duckduckgo",
"enabled": true,
"openaiCodex": {}
},
"fetch": {
"enabled": true
}
},
"media": {
"models": [
{
"type": "cli",
"command": "whisper-cli",
"args": [
"-m",
"/Users/nmaximo7/macOS/tools/ggml-large-v3-turbo.bin",
"-f",
"{{AttachmentPath}}",
"-l",
"auto",
"-otxt"
],
"timeoutSeconds": 300,
"capabilities": ["audio"]
},
{
"provider": "ollama",
"model": "qwen3.8:27b",
"capabilities": ["image"]
}
],
"audio": {
"enabled": true,
"maxBytes": 20971520,
"preferredModel": "cli:whisper-cli"
},
"image": {
"enabled": true,
"maxBytes": 10485760,
"preferredModel": "qwen3.8:27b"
}
}
}
}
This is more robust than a custom skill because:
To apply that JSON block globally, we can add it to your OpenClaw configuration file (typically located at ~/.openclaw/config.json or within your active profile’s config).
requires.bins for gating. If gog or qmd is missing, the skill will not load, which is better than failing at runtime.description under 160 characters and one line.SKILL.md. If the briefing grows, move the detailed command reference into a separate file in the skill directory and reference it with {baseDir}.openclaw agent --message "..." before sharing---
name: daily-briefing
description: "Produce a concise daily briefing (calendar, unread mail, weather, todo list) when the user asks for their day ahead or daily routine. Under 200 words, Europe/Madrid time, confirmed facts only."
metadata: {"openclaw":{"emoji":"☀️","os":["darwin"],"requires":{"bins":["gog","qmd"]}}}
---
# Daily Briefing
Trigger when the user asks for their **daily routine**, **day ahead**, **morning briefing**, or **what's on today**.
## Preflight
Before fetching anything, confirm the tools are usable:
```bash
gog auth list --check --json --no-input >/dev/null 2>&1 || echo "GOG_AUTH_FAILED"
command -v qmd >/dev/null 2>&1 || echo "QMD_MISSING"
```
If GOG_AUTH_FAILED appears, say “Calendar and mail are unavailable — gog needs re-authentication” and continue with weather and todo. Never report an auth failure as “nothing scheduled.”
## Timezone
The user is in Europe/Madrid (UTC+01:00, or UTC+02:00 in summer). Use the system’s local time for the header line. Label the timezone explicitly. Never report a time without a zone.
## Sources (fetch in parallel where possible)
### 1. Google Calendar — today + next 7 days
```sh
gog cal list --days 8 --json
```
- Summarize by date.
- If the result is empty, say “nothing scheduled” — do not invent a quiet week.
- Flag anything starting within 2 hours as urgent.
### 2. Gmail — unread and recent
```sh
gog gmail search 'in:inbox' --limit 8 --json
```
- Show the top few, newest first.
- Skip newsletters and promotions unless they are flagged.
- Do not reproduce verification codes, security alerts, or the OAuth grant notice verbatim. Summarise them as “security/verification noise.”
- Flag anything that clearly needs a reply or an action.
### 3. Weather — today's forecast
Use `web_fetch` with wttr.in (Europe/Madrid), format `j2`:
```javascript
await web_fetch({ url: "https://wttr.in/Madrid?format=j2", extractMode: "text", maxChars: 12000 });
```
- Report: condition, temperature range (min–max), rain risk, and wind.
- If wttr.in fails, retry `https://wttr.is/Madrid?format=j2`. If both fail, say "weather unavailable" — do not guess.
### 4. Todo list
```sh
qmd get macos/docs/assets/Todo.md
```
Note: qmd get returns the full document by default; --full is optional and may not be accepted by every build. Pull the file and extract only the actionable lines. If the file is missing, fall back to:
```sh
qmd search "Todo" -c macos --json
```
If both fail, say “todo list unavailable.”
## Output format
Scannable, under ~200 words. Structure:
```
☀️ Briefing — <day>, <date> · <time> Europe/Madrid
📅 Today
- <event or "nothing scheduled">
📅 This week
- <compact list, date + title>
📬 Mail
- <3-5 lines max>
🌤 Weather
- <condition, range, rain risk>
✅ Todo
- <top 3 actionable items>
💡 3 things to prepare for
1. ...
2. ...
3. ...
```
Rules:
- Confirmed facts only. Label anything inferred as `[inferred]`.
- Sources unavailable → say so. Never fill the gap with invented content.
- No raw verification codes or full security-alert bodies.
- If a source fails, still produce the briefing with the remaining sections.
## Verification
After writing, check:
1. Every section either cites a real source or explicitly says it is unavailable.
2. The word count is under 200.
3. No time appears without a timezone label.
4. No verification codes or security-alert bodies appear verbatim.