Skip to main content
Learn from actual skills in the NanoClaw repository. These examples cover the four skill types: feature, utility, operational, and container.

Interactive skill: setup

The /setup skill guides users through initial NanoClaw installation. It’s an operational skill — a SKILL.md on main with no code changes.

Key characteristics

  • Multi-phase workflow (11 distinct steps)
  • Heavy error recovery and troubleshooting
  • Platform detection (macOS vs Linux vs WSL)
  • User authentication flows
  • Service management

Structure

---
name: setup
description: Run initial NanoClaw setup. Use when user wants to install dependencies, authenticate messaging channels, register their main channel, or start the background services.
---

# NanoClaw Setup

**Principle:** When something is broken or missing, fix it. Don't tell the user to go fix it themselves unless it genuinely requires their manual action.

**UX Note:** Use `AskUserQuestion` for all user-facing questions.

## 1. Bootstrap (Node.js + Dependencies)

Run `bash setup.sh` and parse the status block.

- If NODE_OK=false → Node.js is missing or too old. Use `AskUserQuestion: Would you like me to install Node.js 22?`
  - macOS: `brew install node@22` or install nvm
  - Linux: `curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -`
...

Pattern: Status block parsing

The skill runs scripts and parses structured output:
--- STATUS ---
NODE_OK=true
DEPS_OK=true
NATIVE_OK=true
PLATFORM=macos
IS_WSL=false
--- END STATUS ---

Pattern: Conditional flows

Different platforms require different commands:
### 3a. Choose runtime

- PLATFORM=linux → Docker (only option)
- PLATFORM=macos + APPLE_CONTAINER=installed → 
  AskUserQuestion: Docker (default) or Apple Container?
- PLATFORM=macos + APPLE_CONTAINER=not_found → Docker (default)

Pattern: Error recovery

Comprehensive troubleshooting for each failure mode:
**If BUILD_OK=false:** Read `logs/setup.log` tail for the build error.
- Cache issue: `docker builder prune -f` and retry
- Dockerfile syntax: diagnose from log and fix, then retry

**If TEST_OK=false but BUILD_OK=true:** The image built but won't run.
- Check logs — common cause is runtime not fully started
- Wait a moment and retry the test

Feature skill: add-telegram

The /add-telegram skill adds Telegram channel support by merging the skill/telegram branch.

What the skill branch contains

The skill/telegram branch contains all code changes needed for Telegram support:
skill/telegram branch
src/channels/telegram.ts
src/channels/telegram.test.ts
src/index.ts
src/config.ts
src/routing.test.ts
package.json
.env.example
You can inspect the full diff before applying:
git diff main...upstream/skill/telegram

SKILL.md phases

1

Phase 1: Pre-flight

## Phase 1: Pre-flight

### Check if already applied

Check git history for a previous merge of skill/telegram.

### Ask the user

AskUserQuestion: Should Telegram replace existing channels or run alongside them?
- **Replace existing channels** - sets TELEGRAM_ONLY=true
- **Alongside** - both channels active

AskUserQuestion: Do you have a Telegram bot token, or do you need to
create one?
2

Phase 2: Merge the skill branch

git fetch upstream skill/telegram
git merge upstream/skill/telegram
This adds:
  • src/channels/telegram.ts (TelegramChannel class)
  • src/channels/telegram.test.ts (46 unit tests)
  • Multi-channel support in src/index.ts
  • Telegram config in src/config.ts
  • The grammy npm dependency
Validate:
npm install
npm test
npm run build
3

Phase 3: Setup

Create Telegram Bot (if needed):If the user doesn’t have a bot token, tell them:
  1. Open Telegram and search for @BotFather
  2. Send /newbot and follow prompts
  3. Copy the bot token
Configure environment by adding to .env:
TELEGRAM_BOT_TOKEN=<their-token>
Sync to container environment:
mkdir -p data/env && cp .env data/env/env
4

Phase 4: Registration

Get the Chat ID:Tell the user:
  1. Open your bot in Telegram
  2. Send /chatid — it will reply with the chat ID
  3. For groups: add the bot first, then send /chatid
Wait for the user to provide the chat ID.Register the chat:
registerGroup("tg:<chat-id>", {
  name: "<chat-name>",
  folder: "main",
  trigger: `@${ASSISTANT_NAME}`,
  added_at: new Date().toISOString(),
  requiresTrigger: false,
});
5

Phase 5: Verify

Test the connection:Tell the user to send a message to their registered Telegram chat. The bot should respond within a few seconds.Check logs if needed:
tail -f logs/nanoclaw.log

Interactive skill: customize

The /customize skill is an interactive guide for common customizations.

Key patterns

---
name: customize
description: Add new capabilities or modify NanoClaw behavior. Use when user wants to add channels, change triggers, add integrations, or make any other customizations.
---

# NanoClaw Customization

## Workflow

1. **Understand the request** - Ask clarifying questions
2. **Plan the changes** - Identify files to modify
3. **Implement** - Make changes directly to the code
4. **Test guidance** - Tell user how to verify

## Common Customization Patterns

### Adding a New Input Channel

Questions to ask:
- Which channel? (Telegram, Slack, Discord, email, SMS, etc.)
- Same trigger word or different?
- Same memory hierarchy or separate?

Implementation pattern:
1. Create `src/channels/{name}.ts` implementing the `Channel` interface
2. Add the channel instance to `main()` in `src/index.ts`
3. Messages are stored via the `onMessage` callback

Pattern: Guided workflows

For each customization type, provide:
  1. Questions to ask - What information do you need?
  2. Implementation pattern - How to make the changes
  3. Verification - How to test it works
### Adding a New Input Channel

Questions to ask:
- Which channel?
- Same trigger word or different?
- Same memory hierarchy or separate?

Implementation pattern:
1. Create `src/channels/{name}.ts`
2. Add to `main()` in `src/index.ts`
3. Wire callbacks

Feature skill: add-slack

The /add-slack skill shows a complete channel integration with detailed setup documentation.

Supplementary documentation

The skill’s SKILL.md in the marketplace includes a reference to a setup guide: From SKILL.md:
### Create Slack App (if needed)

If the user doesn't have a Slack app, share [SLACK_SETUP.md](SLACK_SETUP.md) 
which has step-by-step instructions with screenshots guidance, troubleshooting, 
and a token reference table.

Quick summary of what's needed:
1. Create a Slack app at api.slack.com/apps
2. Enable Socket Mode and generate an App-Level Token
3. Subscribe to bot events: `message.channels`, `message.groups`, `message.im`
4. Add OAuth scopes: `chat:write`, `channels:history`, etc.
5. Install to workspace and copy the Bot Token

Known limitations

The skill documents limitations clearly:
## Known Limitations

- **Threads are flattened** — Threaded replies are delivered to the agent 
  as regular channel messages. The agent sees them but has no awareness 
  they originated in a thread. Responses always go to the channel, not 
  back into the thread.
  
- **No typing indicator** — Slack's Bot API does not expose a typing 
  indicator endpoint. The `setTyping()` method is a no-op.
  
- **Message splitting is naive** — Long messages are split at a fixed 
  4000-character boundary, which may break mid-word or mid-sentence.
  
- **No file/image handling** — The bot only processes text content. File 
  uploads, images, and rich message blocks are not forwarded to the agent.

Operational skill with bundled script: claw

The /claw skill installs a Python CLI tool that runs NanoClaw agents from the terminal — no chat app required. It’s an operational skill on main with a bundled script.

Key characteristics

  • Ships a Python script alongside the SKILL.md
  • Reads from NanoClaw’s SQLite database and .env at runtime
  • Auto-detects the container runtime (docker or container)
  • Parses structured output sentinels from the container

Structure

.claude/skills/claw
SKILL.md
scripts
claw

Pattern: Bundled scripts

Unlike instruction-only skills, /claw includes a script that Claude copies into the project and symlinks into PATH:
### 1. Copy the script

mkdir -p scripts
cp "${CLAUDE_SKILL_DIR}/scripts/claw" scripts/claw
chmod +x scripts/claw

### 2. Symlink into PATH

mkdir -p ~/bin
ln -sf "$(pwd)/scripts/claw" ~/bin/claw
The CLAUDE_SKILL_DIR variable resolves to the skill directory at runtime, so the symlink always points to the right place.

Pattern: Database integration

The script reads registered groups directly from NanoClaw’s SQLite database:
def get_groups(db: Path) -> list[dict]:
    conn = sqlite3.connect(db)
    rows = conn.execute(
        "SELECT jid, name, folder, is_main FROM registered_groups ORDER BY name"
    ).fetchall()
    conn.close()
    return [{"jid": r[0], "name": r[1], "folder": r[2], "is_main": bool(r[3])} for r in rows]
This means claw works with any channel — it targets groups by name, folder, or JID without needing to know which messaging platform is connected.

Pattern: Container output sentinel

The script uses output sentinels to know when the agent has finished:
if line.strip() == "---NANOCLAW_OUTPUT_END---":
    dbg("output sentinel found, terminating container")
    done.set()
    proc.kill()
This lets the CLI kill the container immediately once output is received, rather than waiting for the Node.js event loop to exit naturally. Learn more about the claw CLI in the command-line interface guide.

Advanced skill: add-parallel

The /add-parallel skill adds an MCP integration with non-blocking async operations.

Key techniques

### 3. Configure Environment

Add `PARALLEL_API_KEY` to `.env` so MCP env resolution can access it:

```bash
# .env
PARALLEL_API_KEY=your-api-key-here
The readMcpEnvVars() function in container-runner.ts automatically extracts ${VAR} references from .mcp.json and resolves them from .env.

</Tab>

<Tab title="MCP server config">

```markdown
### 4. Configure MCP Servers

Add HTTP MCP servers:

```typescript
if (parallelApiKey) {
  mcpServers['parallel-search'] = {
    type: 'http',  // REQUIRED for HTTP servers
    url: 'https://search-mcp.parallel.ai/mcp',
    headers: {
      'Authorization': `Bearer ${parallelApiKey}`
    }
  };
  mcpServers['parallel-task'] = {
    type: 'http',
    url: 'https://task-mcp.parallel.ai/mcp',
    headers: {
      'Authorization': `Bearer ${parallelApiKey}`
    }
  };
}

</Tab>

<Tab title="Non-blocking async">

```markdown
### Deep Research - DO NOT BLOCK!

**After permission - Use scheduler instead:**

1. Create the task using `mcp__parallel-task__create_task_run`
2. Get the `run_id` from the response
3. Create a polling scheduled task:
Prompt: “Check Parallel AI task run [run_id] and send results when ready.
  1. Use the Parallel Task MCP to check the task status
  2. If status is ‘completed’, extract the results
  3. Send results with mcp__nanoclaw__send_message
  4. Use mcp__nanoclaw__cancel_task to remove the polling task
If status is still ‘running’, do nothing (task will run again in 30s).”Schedule: interval every 30 seconds Context mode: isolated
4. Send acknowledgment with tracking link
5. Exit immediately - scheduler handles the rest

Pattern summary

All skills follow these principles from the NanoClaw philosophy:
  • Fix problems automatically when possible
  • Use AskUserQuestion for all user input
  • Provide comprehensive troubleshooting
  • Handle platform differences (macOS/Linux)
  • Verify changes with tests and logs

Common patterns

PatternUsed inPurpose
Status block parsing/setupParse structured command output
Git branch merge/add-telegram, /add-slackApply code changes via git merge
AskUserQuestionAll skillsGet user input consistently
Multi-phase structureMost skillsOrganize complex workflows
Platform detection/setupHandle macOS/Linux differences
Verification stepsAll skillsConfirm changes worked
Troubleshooting sectionAll skillsHelp users fix common issues

Next steps

Last modified on March 24, 2026