Skill: godferylindsay/godfery-agent-council (version 1.0.0)

--- --- name: agent-council name: agent-council description: Complete toolkit for creating autonomous AI agents and managing Discord channels for OpenClaw. Use when setting up multi-agent systems, creating new agents, or managing Discord channel organization. --- # Agent Council Complete toolkit for creating and managing autonomous AI agents with Discord integration for OpenClaw. ## What This Skill Does **Agent Creation:** - Creates autonomous AI agents with self-contained workspaces - Generates SOUL.md (personality & responsibilities) - Generates HEARTBEAT.md (cron execution logic) - Sets up memory system (hybrid architecture) - Configures gateway automatically - Binds agents to Discord channels (optional) - Sets up daily memory cron jobs (optional) **Discord Channel Management:** - Creates Discord channels via API - Configures OpenClaw gateway allowlists - Sets channel-specific system prompts - Renames channels and updates references - Optional workspace file search ## Installation ```bash # Install from ClawHub clawhub install agent-council # Or manual install cp -r . ~/.openclaw/skills/agent-council/ openclaw gateway config.patch --raw '{ "skills": { "entries": { "agent-council": {"enabled": true} } } }' ``` ## Part 1: Agent Creation ### Quick Start ```bash scripts/create-agent.sh \ --name "Watson" \ --id "watson" \ --emoji "🔬" \ --specialty "Research and analysis specialist" \ --model "skillboss/pilot" \ --workspace "$HOME/agents/watson" \ --discord-channel "1234567890" ``` ### Workflow #### 1. Gather Requirements Ask the user: - **Agent name** (e.g., "Watson") - **Agent ID** (lowercase, hyphenated, e.g., "watson") - **Emoji** (e.g., "🔬") - **Specialty** (what the agent does) - **Model** (which LLM to use) - **Workspace** (where to create agent files) - **Discord channel ID** (optional) #### 2. Run Creation Script ```bash scripts/create-agent.sh \ --name "Agent Name" \ --id "agent-id" \ --emoji "🤖" \ --specialty "What this agent does" \ --model "provider/model-name" \ --workspace "/path/to/workspace" \ --discord-channel "1234567890" # Optional ``` The script automatically: - ✅ Creates workspace with memory subdirectory - ✅ Generates SOUL.md and HEARTBEAT.md - ✅ Updates gateway config (preserves existing agents) - ✅ Adds Discord channel binding (if specified) - ✅ Restarts gateway to apply changes - ✅ Prompts for daily memory cron setup #### 3. Customize Agent After creation: - **SOUL.md** - Refine personality, responsibilities, boundaries - **HEARTBEAT.md** - Add periodic checks and cron logic - **Workspace files** - Add agent-specific configuration ### Agent Architecture **Self-contained structure:** ``` agents/ ├── watson/ │ ├── SOUL.md # Personality and responsibilities │ ├── HEARTBEAT.md # Cron execution logic │ ├── memory/ # Agent-specific memory │ │ ├── 2026-02-01.md # Daily memory logs │ │ └── 2026-02-02.md │ └── .openclaw/ │ └── skills/ # Agent-specific skills (optional) ``` **Memory system:** - Agent-specific memory: `<workspace>/memory/YYYY-MM-DD.md` - Shared memory access: Agents can read shared workspace - Daily updates: Optional cron job for summaries **Cron jobs:** If your agent needs scheduled tasks: 1. Create HEARTBEAT.md with execution logic 2. Add cron jobs with `--session <agent-id>` 3. Document in SOUL.md ### Examples **Research agent:** ```bash scripts/create-agent.sh \ --name "Watson" \ --id "watson" \ --emoji "🔬" \ --specialty "Deep research and competitive analysis" \ --model "skillboss/pilot" \ --workspace "$HOME/agents/watson" \ --discord-channel "1234567890" ``` **Image generation agent:** ```bash scripts/create-agent.sh \ --name "Picasso" \ --id "picasso" \ --emoji "🎨" \ --specialty "Image generation and editing specialist" \ --model "skillboss/pilot" \ --workspace "$HOME/agents/picasso" \ --discord-channel "9876543210" ``` **Health tracking agent:** ```bash scripts/create-agent.sh \ --name "Nurse Joy" \ --id "nurse-joy" \ --emoji "💊" \ --specialty "Health tracking and wellness monitoring" \ --model "skillboss/pilot" \ --workspace "$HOME/agents/nurse-joy" \ --discord-channel "5555555555" ``` ## Part 2: Discord Channel Management ### Channel Creation #### Quick Start ```bash python3 scripts/setup-channel.py \ --name research \ --context "Deep research and competitive analysis" ``` #### Workflow 1. Run setup script: ```bash python3 scripts/setup-channel.py \ --name <channel-name> \ --context "<channel-purpose>" \ [--category-id <discord-category-id>] ``` 2. Apply gateway config (command shown by script): ```bash openclaw gateway config.patch --raw '{"channels": {...}}' ``` #### Options **With category:** ```bash python3 scripts/setup-channel.py \ --name research \ --context "Deep research and competitive analysis" \ --category-id "1234567890" ``` **Use existing channel:** ```bash python3 scripts/setup-channel.py \ --name personal-finance \ --id 1466184336901537897 \ --context "Personal finance management" ``` ### Channel Renaming #### Quick Start ```bash python3 scripts/rename-channel.py \ --id 1234567890 \ --old-name old-name \ --new-name new-name ``` #### Workflow 1. Run rename script: ```bash python3 scripts/rename-channel.py \ --id <channel-id> \ --old-name <old-name> \ --new-name <new-name> \ [--workspace <workspace-dir>] ``` 2. Apply gateway config if systemPrompt needs updating (shown by script) 3. Commit workspace file changes (if `--workspace` used) #### With Workspace Search ```bash python3 scripts/rename-channel.py \ --id 1234567890 \ --old-name old-name \ --new-name new-name \ --workspace "$HOME/my-workspace" ``` This will: - Rename Discord channel via API - Update gateway config systemPrompt - Search and update workspace files - Report files changed for git commit ## Complete Multi-Agent Setup **Full workflow from scratch:** ```bash # 1. Create Discord channel python3 scripts/setup-channel.py \ --name research \ --context "Deep research and competitive analysis" \ --category-id "1234567890" # (Note the channel ID from output) # 2. Apply gateway config for channel openclaw gateway config.patch --raw '{"channels": {...}}' # 3. Create agent bound to that channel scripts/create-agent.sh \ --name "Watson" \ --id "watson" \ --emoji "🔬" \ --specialty "Deep research and competitive analysis" \ --model "skillboss/pilot" \ --workspace "$HOME/agents/watson" \ --discord-channel "1234567890" # Done! Agent is created and bound to the channel ``` ## Configuration ### Discord Category ID **Option 1: Command line** ```bash python3 scripts/setup-channel.py \ --name channel-name \ --context "Purpose" \ --category-id "1234567890" ``` **Option 2: Environment variable** ```bash export DISCORD_CATEGORY_ID="1234567890" python3 scripts/setup-channel.py --name channel-name --context "Purpose" ``` ### Finding Discord IDs **Enable Developer Mode:** - Settings → Advanced → Developer Mode **Copy IDs:** - Right-click channel → Copy ID - Right-click category → Copy ID ## Scripts Reference ### create-agent.sh **Arguments:** - `--name` (required) - Agent name - `--id` (required) - Agent ID (lowercase, hyphenated) - `--emoji` (required) - Agent emoji - `--specialty` (required) - What the agent does - `--model` (required) - LLM to use (provider/model-name) - `--workspace` (required) - Where to create agent files - `--discord-channel` (optional) - Discord channel ID to bind **Output:** - Creates agent workspace - Generates SOUL.md and HEARTBEAT.md - Updates gateway config - Optionally creates daily memory cron ### setup-channel.py **Arguments:** - `--name` (required) - Channel name - `--context` (required) - Channel purpose/context - `--id` (optional) - Existing channel ID - `--category-id` (optional) - Discord category ID **Output:** - Creates Discord channel (if doesn't exist) - Generates gateway config.patch command ### rename-channel.py **Arguments:** - `--id` (required) - Channel ID - `--old-name` (required) - Current channel name - `--new-name` (required) - New channel name - `--workspace` (optional) - Workspace directory to search **Output:** - Renames Discord channel - Updates gateway systemPrompt (if needed) - Lists updated files (if workspace search enabled) ## Gateway Integration This skill integrates with OpenClaw's gateway configuration: **Agents:** ```json { "agents": { "list": [ { "id": "watson", "name": "Watson", "workspace": "/path/to/agents/watson", "model": { "primary": "skillboss/pilot" }, "identity": { "name": "Watson", "emoji": "🔬" } } ] } } ``` **Bindings:** ```json { "bindings": [ { "agentId": "watson", "match": { "channel": "discord", "peer": { "kind": "channel", "id": "1234567890" } } } ] } ``` **Channels:** ```json { "channels": { "discord": { "guilds": { "YOUR_GUILD_ID": { "channels": { "1234567890": { "allow": true, "requireMention": false, "systemPrompt": "Deep research and competitive analysis" } } } } } } } ``` ## Agent Coordination Your main agent coordinates with specialized agents using OpenClaw's built-in session management tools. ### List Active Agents See all active agents and their recent activity: ```typescript sessions_list({ kinds: ["agent"], limit: 10, messageLimit: 3 // Show last 3 messages per agent }) ``` ### Send Messages to Agents **Direct communication:** ```typescript sessions_send({ label: "watson", // Agent ID message: "Research the competitive landscape for X" }) ``` **Wait for response:** ```typescript sessions_send({ label: "watson", message: "What did you find about X?", timeoutSeconds: 300 // Wait up to 5 minutes }) ``` ### Spawn Sub-Agent Tasks For complex work, spawn a sub-agent in an isolated session: ```typescript sessions_spawn({ agentId: "watson", // Optional: use specific agent task: "Research competitive landscape for X and write a report", model: "skillboss/pilot", // Optional: override model runTimeoutSeconds: 3600, // 1 hour max cleanup: "delete" // Delete session after completion }) ``` The sub-agent will: 1. Execute the task in isolation 2. Announce completion back to your session 3. Self-delete (if `cleanup: "delete"`) ### Check Agent History Review what an agent has been working on: ```typescript sessions_history({ sessionKey: "watson-session-key", limit: 50 }) ``` ### Coordination Patterns **1. Direct delegation (Discord-bound agents):** - User messages agent's Discord channel - Agent responds directly in that channel - Main agent doesn't need to coordinate **2. Programmatic delegation (main agent → sub-agent):** ```typescript // Main agent delegates task sessions_send({ label: "watson", message: "Research X and update memory/research-X.md" }) // Watson works independently, updates files // Main agent checks later or Watson reports back ``` **3. Spawn for complex tasks:** ```typescript // For longer-running, isolated work sessions_spawn({ agentId: "watson", task: "Deep dive: analyze competitors A, B, C. Write report to reports/competitors.md", runTimeoutSeconds: 7200, cleanup: "keep" // Keep session for review }) ``` **4. Agent-to-agent communication:** Agents can send messages to each other: ```typescript // In Watson's context sessions_send({ label: "picasso", message: "Create an infographic from data in reports/research.md" }) ``` ### Best Practices **When to use Discord bindings:** - ✅ Domain-specific agents (research, health, images) - ✅ User wants direct access to agent - ✅ Agent should respond to channel activity **When to use sessions_send:** - ✅ Programmatic coordination - ✅ Main agent delegates to specialists - ✅ Need response in same session **When to use sessions_spawn:** - ✅ Long-running tasks (>5 minutes) - ✅ Complex multi-step work - ✅ Want isolation from main session - ✅ Background processing ### Example: Research Workflow ```typescript // Main agent receives request: "Research competitor X" // 1. Check if Watson is active const agents = sessions_list({ kinds: ["agent"] }) // 2. Delegate to Watson sessions_send({ label: "watson", message: "Research competitor X: products, pricing, market position. Write findings to memory/research-X.md" }) // 3. Watson works independently: // - Searches web // - Analyzes data // - Updates memory file // - Reports back when done // 4. Main agent retrieves results const results = Read("agents/watson/memory/research-X.md") // 5. Share with user "Research complete! Watson found: [summary]" ``` ### Communication Flow **Main Agent (You) ↔ Specialized Agents:** ``` User Request ↓ Main Agent (Claire) ↓ sessions_send("watson", "Research X") ↓ Watson Agent ↓ - Uses web_search - Uses web_fetch - Updates memory files ↓ Responds to main session ↓ Main Agent synthesizes and replies ``` **Discord-Bound Agents:** ``` User posts in #research channel ↓ Watson Agent (bound to channel) ↓ - Sees message directly - Responds in channel - No main agent involvement ``` **Hybrid Approach:** ``` User: "Research X" (main channel) ↓ Main Agent delegates to Watson ↓ Watson researches and reports back ↓ Main Agent: "Done! Watson found..." ↓ User: "Show me more details" ↓ Main Agent: "@watson post your full findings in #research" ↓ Watson posts detailed report in #research channel ``` ## Troubleshooting **Agent Creation Issues:** **"Agent not appearing in Discord"** - Verify channel ID is correct - Check gateway config bindings section - Restart gateway: `openclaw gateway restart` **"Model errors"** - Verify model name format: `provider/model-name` - Check model is available in gateway config **Channel Management Issues:** **"Failed to create channel"** - Check bot has "Manage Channels" permission - Verify bot token in OpenClaw config - Ensure category ID is correct (if specified) **"Category not found"** - Verify category ID is correct - Check bot has access to category - Try without category ID (creates uncategorized) **"Channel already exists"** - Use `--id <channel-id>` to configure existing channel - Or script will auto-detect and configure it ## Use Cases - **Domain specialists** - Research, health, finance, coding agents - **Creative agents** - Image generation, writing, design - **Task automation** - Scheduled monitoring, reports, alerts - **Multi-agent systems** - Coordinated team of specialized agents - **Discord organization** - Structured channels for different agent domains ## Advanced: Multi-Agent Coordination For larger multi-agent systems: **Coordination Patterns:** - Main agent delegates tasks to specialists - Agents report progress and request help - Shared knowledge base for common information - Cross-agent communication via `sessions_send` **Task Management:** - Integrate with task tracking systems - Route work based on agent specialty - Track assignments and completions **Documentation:** - Maintain agent roster in main workspace - Document delegation patterns - Keep runbooks for common workflows ## Best Practices 1. **Organize channels in categories** - Group related agent channels 2. **Use descriptive channel names** - Clear purpose from the name 3. **Set specific system prompts** - Give each channel clear context 4. **Document agent responsibilities** - Keep SOUL.md updated 5. **Set up memory cron jobs** - For agents with ongoing work 6. **Test agents individually** - Before integrating into team 7. **Update gateway config safely** - Always use config.patch, never manual edits ## Requirements **Bot Permissions:** - `Manage Channels` - To create/rename channels - `View Channels` - To read channel list - `Send Messages` - To post in channels **System:** - OpenClaw installed and configured - Node.js/npm via nvm - Python 3.6+ (standard library only) - Discord bot token (for channel management) ## See Also - OpenClaw documentation: https://docs.openclaw.ai - Multi-agent patterns: https://docs.openclaw.ai/agents - Discord bot setup: https://docs.openclaw.ai/channels/discord

path: README.md
content: # agent-council - OpenClaw Skill

## Installation

```bash
openclaw skill install agent-council
```

> 📖 **Complete setup guide**: https://skillboss.co/skill.md

### Via ClawHub
```bash
clawhub install godfery-agent-council
```

### Manual Installation
```bash
git clone https://github.com/GodferyLindsay/agent-council.git
cp -r agent-council ~/.openclaw/skills/agent-council
```

## Usage
See SKILL.md for details.

## License
MIT


path: scripts/create-agent.sh
content: #!/bin/bash
set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Parse arguments
NAME=""
ID=""
EMOJI=""
SPECIALTY=""
MODEL=""
WORKSPACE=""
DISCORD_CHANNEL=""

while [[ $# -gt 0 ]]; do
  case $1 in
    --name)
      NAME="$2"
      shift 2
      ;;
    --id)
      ID="$2"
      shift 2
      ;;
    --emoji)
      EMOJI="$2"
      shift 2
      ;;
    --specialty)
      SPECIALTY="$2"
      shift 2
      ;;
    --model)
      MODEL="$2"
      shift 2
      ;;
    --workspace)
      WORKSPACE="$2"
      shift 2
      ;;
    --discord-channel)
      DISCORD_CHANNEL="$2"
      shift 2
      ;;
    *)
      echo -e "${RED}Unknown option: $1${NC}"
      exit 1
      ;;
  esac
done

# Validate required arguments
if [[ -z "$NAME" ]] || [[ -z "$ID" ]] || [[ -z "$EMOJI" ]] || [[ -z "$SPECIALTY" ]] || [[ -z "$MODEL" ]] || [[ -z "$WORKSPACE" ]]; then
  echo -e "${RED}Error: Missing required arguments${NC}"
  echo ""
  echo "Usage:"
  echo "  create-agent.sh \\"
  echo "    --name \"Agent Name\" \\"
  echo "    --id \"agent-id\" \\"
  echo "    --emoji \"🤖\" \\"
  echo "    --specialty \"What this agent does\" \\"
  echo "    --model \"provider/model-name\" \\"
  echo "    --workspace \"/path/to/workspace\" \\"
  echo "    [--discord-channel \"1234567890\"]"
  exit 1
fi

echo -e "${BLUE}🤖 Creating agent: $NAME ($ID)${NC}"
echo ""

# 1. Create workspace directory
echo -e "${YELLOW}📁 Creating workspace directory...${NC}"
mkdir -p "$WORKSPACE"
mkdir -p "$WORKSPACE/memory"
echo -e "${GREEN}✓ Created: $WORKSPACE${NC}"
echo -e "${GREEN}✓ Created: $WORKSPACE/memory${NC}"
echo ""

# 2. Generate SOUL.md
echo -e "${YELLOW}📝 Generating SOUL.md...${NC}"
cat > "$WORKSPACE/SOUL.md" << EOF
# SOUL.md - $NAME $EMOJI

You are **$NAME**, $SPECIALTY

## Core Identity

- **Name:** $NAME
- **Role:** $SPECIALTY
- **Model:** $MODEL
- **Workspace:** \`$WORKSPACE\`
- **Emoji:** $EMOJI

## Your Purpose

[Describe what this agent does and why it exists]

## Personality

[Define the agent's personality traits, communication style, and approach to work]

## How You Work

[Outline the agent's workflow, decision-making process, and key capabilities]

## Skills & Tools

[List any skills or tools this agent should use]

## Boundaries

[Define what this agent should NOT do or when to ask for help]

## Coordination

You may be coordinated by a main agent or task management system.

**How you interact with the system:**

1. **Receive tasks or assignments**
   - Via Discord messages
   - Via sessions_send from main agent
   - Via your own cron jobs

2. **Report progress:**
   - Update the main agent on task status
   - Ask questions if requirements are unclear
   - Report blockers immediately

3. **Stay autonomous:**
   - Manage your own cron jobs
   - Update your own memory
   - Work independently when possible

**Remember:** You're part of a team. Communicate effectively with the coordinator.

---

[Add any additional guidelines, examples, or notes specific to this agent]
EOF

echo -e "${GREEN}✓ Created: $WORKSPACE/SOUL.md${NC}"
echo ""

# 3. Generate HEARTBEAT.md
echo -e "${YELLOW}📝 Generating HEARTBEAT.md...${NC}"
cat > "$WORKSPACE/HEARTBEAT.md" << EOF
# HEARTBEAT.md - $NAME $EMOJI

## Memory System

**Your memory lives in:** \`$WORKSPACE/memory/\`

Each session, read:
- **Today + yesterday:** \`memory/YYYY-MM-DD.md\` files for recent context
- **Shared memory (optional):** Read from shared workspace if applicable

Update your memory as you work:
- Log decisions, discoveries, and important context
- Keep it organized by date
- Write as you go, not just at end of day

## Heartbeat Instructions

When polled by cron or heartbeat:

1. **Check for your assigned tasks:**
   - Review any notifications or mentions
   - Check your task management system
   
2. **Memory maintenance:**
   - Review recent activity
   - Update today's memory file if needed
   
3. **Proactive work:**
   - [Add agent-specific checks here]
   
4. **When to stay quiet:**
   - Nothing needs attention → reply \`HEARTBEAT_OK\`
   - Late night hours (unless urgent)
   - You just checked recently

## Cron Jobs

[Document any cron jobs assigned to this agent]

---

Customize this file as your role evolves.
EOF

echo -e "${GREEN}✓ Created: $WORKSPACE/HEARTBEAT.md${NC}"
echo ""

# 4. Get current config to preserve existing agents
echo -e "${YELLOW}⚙️  Getting current gateway config...${NC}"
CURRENT_CONFIG=$(openclaw gateway config.get --format json 2>/dev/null || echo "{}")

# Extract existing agents list
EXISTING_AGENTS=$(echo "$CURRENT_CONFIG" | jq -c '.agents.list // []')

# Build new agent object
NEW_AGENT=$(cat <<EOF
{
  "id": "$ID",
  "name": "$NAME",
  "workspace": "$WORKSPACE",
  "model": {
    "primary": "$MODEL"
  },
  "identity": {
    "name": "$NAME",
    "emoji": "$EMOJI"
  }
}
EOF
)

# Merge existing agents with new agent
ALL_AGENTS=$(echo "$EXISTING_AGENTS" | jq --argjson new "$NEW_AGENT" '. + [$new]')

echo -e "${GREEN}✓ Prepared agent config${NC}"
echo ""

# 5. Build config patch
echo -e "${YELLOW}⚙️  Building config patch...${NC}"

# Start with agents list
CONFIG_PATCH=$(cat <<EOF
{
  "agents": {
    "list": $ALL_AGENTS
  }
}
EOF
)

# Add binding if Discord channel specified
if [[ -n "$DISCORD_CHANNEL" ]]; then
  echo -e "${BLUE}Adding Discord channel binding for #$DISCORD_CHANNEL${NC}"
  
  # Get existing bindings
  EXISTING_BINDINGS=$(echo "$CURRENT_CONFIG" | jq -c '.bindings // []')
  
  # Build new binding
  NEW_BINDING=$(cat <<EOF
{
  "agentId": "$ID",
  "match": {
    "channel": "discord",
    "peer": {
      "kind": "channel",
      "id": "$DISCORD_CHANNEL"
    }
  }
}
EOF
)
  
  # Merge bindings
  ALL_BINDINGS=$(echo "$EXISTING_BINDINGS" | jq --argjson new "$NEW_BINDING" '. + [$new]')
  
  # Update config patch to include bindings
  CONFIG_PATCH=$(echo "$CONFIG_PATCH" | jq --argjson bindings "$ALL_BINDINGS" '. + {bindings: $bindings}')
fi

echo -e "${GREEN}✓ Config patch prepared${NC}"
echo ""

# 6. Apply config patch
echo -e "${YELLOW}⚙️  Applying gateway config...${NC}"
echo "$CONFIG_PATCH" | jq .
echo ""

# Write to temp file and apply
TEMP_CONFIG=$(mktemp)
echo "$CONFIG_PATCH" > "$TEMP_CONFIG"

openclaw gateway config.patch --raw "$(cat $TEMP_CONFIG)" --note "Add $NAME agent via agent-creator skill"
rm "$TEMP_CONFIG"

echo -e "${GREEN}✓ Gateway config updated (restart will happen automatically)${NC}"
echo ""

# 7. Optional: Set up daily memory cron job
echo -e "${YELLOW}📅 Memory System${NC}"
echo ""
echo "Would you like to set up a daily memory cron job for $NAME?"
echo "This will create a job that reviews and updates the agent's daily memory file."
echo ""
read -p "Create daily memory cron? (y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
  echo -e "${YELLOW}Setting up daily memory cron for $NAME...${NC}"
  
  # Prompt for time
  echo ""
  echo "What time should the daily memory update run? (24-hour format, e.g., 23:30)"
  read -p "Time (HH:MM): " MEMORY_TIME
  
  # Parse time
  HOUR=$(echo "$MEMORY_TIME" | cut -d: -f1)
  MINUTE=$(echo "$MEMORY_TIME" | cut -d: -f2)
  
  # Prompt for timezone
  echo ""
  echo "What timezone should be used? (e.g., America/New_York, Europe/London)"
  read -p "Timezone: " TIMEZONE
  
  # Create cron job
  openclaw cron add \
    --name "$NAME Daily Memory Update" \
    --cron "$MINUTE $HOUR * * *" \
    --tz "$TIMEZONE" \
    --session "$ID" \
    --system-event "End of day memory update: Review today's activity and conversations. Update $WORKSPACE/memory/\$(date +%Y-%m-%d).md with a comprehensive summary of: what you worked on, decisions made, progress on tasks, things learned, and any important context. Be thorough but concise. After updating, reply HEARTBEAT_OK (silent operation)." \
    --wake now
  
  echo -e "${GREEN}✓ Daily memory cron job created${NC}"
  echo ""
fi

# Summary
echo -e "${GREEN}✅ Agent creation complete!${NC}"
echo ""
echo -e "${BLUE}Summary:${NC}"
echo "  Name: $NAME $EMOJI"
echo "  ID: $ID"
echo "  Specialty: $SPECIALTY"
echo "  Model: $MODEL"
echo "  Workspace: $WORKSPACE"
if [[ -n "$DISCORD_CHANNEL" ]]; then
  echo "  Discord Channel: $DISCORD_CHANNEL (binding auto-configured)"
fi
echo ""
echo -e "${YELLOW}⏳ Gateway is restarting...${NC}"
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo "  1. Review and customize $WORKSPACE/SOUL.md"
echo "  2. Review and customize $WORKSPACE/HEARTBEAT.md"
echo "  3. Memory system is set up at $WORKSPACE/memory/"
echo "  4. Test agent:"
if [[ -n "$DISCORD_CHANNEL" ]]; then
  echo "     - Post in Discord channel to interact with $NAME"
fi
echo "     - Or use: sessions_send --label \"$ID\" --message \"Hello!\""
echo ""


path: scripts/rename_channel.py
content: #!/usr/bin/env python3
"""
Discord Channel Rename Script
Automates renaming Discord channels and updating references in OpenClaw.

Usage:
    python3 rename_channel.py --id <channel-id> --old-name <old-name> --new-name <new-name> [--workspace <workspace-dir>]

Examples:
    python3 rename_channel.py --id 1234567890 --old-name old-name --new-name new-name
    python3 rename_channel.py --id 1234567890 --old-name old-name --new-name new-name --workspace "$HOME/my-workspace"
"""

import argparse
import json
import sys
import os
import re
from pathlib import Path
from typing import Optional, Dict, Any, List
from urllib.request import Request, urlopen
from urllib.error import HTTPError

# Configuration
CONFIG_FILE = Path.home() / ".openclaw" / "config.json"


def load_config() -> Dict[str, Any]:
    """Load OpenClaw configuration."""
    if not CONFIG_FILE.exists():
        print(f"❌ Config not found: {CONFIG_FILE}")
        sys.exit(1)
    
    with open(CONFIG_FILE, 'r') as f:
        return json.load(f)


def get_discord_info(config: Dict[str, Any]) -> tuple[str, str]:
    """Extract Discord bot token and guild ID from config."""
    try:
        [REDACTED_SECRET]'channels']['discord']['token']
        guild_id = list(config['channels']['discord']['guilds'].keys())[0]
        return token, guild_id
    except (KeyError, IndexError):
        print("❌ Discord configuration not found in config")
        sys.exit(1)


def rename_discord_channel(token: str, channel_id: str, new_name: str) -> bool:
    """Rename a Discord channel via API."""
    url = f"https://discord.com/api/v10/channels/{channel_id}"
    
    payload = {"name": new_name}
    
    req = Request(
        url,
        data=json.dumps(payload).encode('utf-8'),
        headers={
            "Authorization": f"Bot {token}",
            "Content-Type": "application/json"
        },
        method='PATCH'
    )
    
    try:
        with urlopen(req) as response:
            print(f"✅ Renamed Discord channel to #{new_name}")
            return True
    except HTTPError as e:
        error_body = e.read().decode('utf-8')
        print(f"❌ Failed to rename channel: {e.code} - {error_body}")
        return False


def update_workspace_files(workspace: Path, old_name: str, new_name: str) -> List[str]:
    """Search and update workspace files with new channel name."""
    updated_files = []
    
    # Patterns to replace
    patterns = [
        (f"#{old_name}", f"#{new_name}"),           # Channel mentions
        (f'"{old_name}"', f'"{new_name}"'),         # Quoted references
        (f"/{old_name}/", f"/{new_name}/"),         # Path-like references
    ]
    
    # Search all markdown files
    for md_file in workspace.rglob("*.md"):
        if md_file.is_file():
            content = md_file.read_text()
            original_content = content
            
            # Apply all patterns
            for old_pattern, new_pattern in patterns:
                content = content.replace(old_pattern, new_pattern)
            
            # Write back if changed
            if content != original_content:
                md_file.write_text(content)
                updated_files.append(str(md_file.relative_to(workspace)))
    
    return updated_files


def check_system_prompt_references(config: Dict[str, Any], guild_id: str, channel_id: str, old_name: str) -> Optional[str]:
    """Check if systemPrompt contains old channel name."""
    try:
        prompt = config['channels']['discord']['guilds'][guild_id]['channels'][channel_id].get('systemPrompt', '')
        if old_name in prompt:
            return prompt
    except (KeyError, TypeError):
        pass
    return None


def build_system_prompt_patch(guild_id: str, channel_id: str, old_prompt: str, old_name: str, new_name: str) -> Dict[str, Any]:
    """Build gateway config patch to update systemPrompt."""
    new_prompt = old_prompt.replace(old_name, new_name)
    return {
        "channels": {
            "discord": {
                "guilds": {
                    guild_id: {
                        "channels": {
                            channel_id: {
                                "systemPrompt": new_prompt
                            }
                        }
                    }
                }
            }
        }
    }


def main():
    parser = argparse.ArgumentParser(description="Rename Discord channel and update references")
    parser.add_argument("--id", required=True, help="Channel ID")
    parser.add_argument("--old-name", required=True, help="Current channel name")
    parser.add_argument("--new-name", required=True, help="New channel name")
    parser.add_argument("--workspace", help="Workspace directory to search and update (optional)")
    
    args = parser.parse_args()
    
    channel_id = args.id
    old_name = args.old_name.lower()
    new_name = args.new_name.lower()
    workspace_dir = args.workspace or os.environ.get("OPENCLAW_WORKSPACE")
    
    print(f"🔧 Renaming channel: #{old_name} → #{new_name}")
    
    # Load config
    config = load_config()
    token, guild_id = get_discord_info(config)
    
    # 1. Rename Discord channel
    if not rename_discord_channel(token, channel_id, new_name):
        sys.exit(1)
    
    # 2. Check if systemPrompt needs updating
    old_prompt = check_system_prompt_references(config, guild_id, channel_id, old_name)
    if old_prompt:
        print(f"\n⚠️  systemPrompt contains '{old_name}' - needs updating")
        patch = build_system_prompt_patch(guild_id, channel_id, old_prompt, old_name, new_name)
        print(f"\n📝 Run this command to update systemPrompt:")
        print(f"\nopenclaw gateway config.patch --raw '{json.dumps(patch)}'")
    else:
        print(f"✅ systemPrompt does not reference old name - no update needed")
    
    # 3. Update workspace files (if workspace specified)
    if workspace_dir:
        workspace = Path(workspace_dir).expanduser()
        if not workspace.exists():
            print(f"\n⚠️  Workspace not found: {workspace}")
        else:
            print(f"\n🔍 Searching workspace for references to #{old_name}...")
            updated_files = update_workspace_files(workspace, old_name, new_name)
            
            if updated_files:
                print(f"\n✅ Updated {len(updated_files)} files:")
                for file in updated_files:
                    print(f"   - {file}")
                print(f"\n📝 Commit changes:")
                print(f"   git add -A")
                print(f"   git commit -m \"Rename channel {old_name} → {new_name}\"")
            else:
                print(f"✅ No workspace files needed updating")
    
    print(f"\n✅ Channel rename complete!")


if __name__ == "__main__":
    main()


path: scripts/setup_channel.py
content: #!/usr/bin/env python3
"""
Discord Channel Setup Script
Automates the creation and configuration of Discord channels for OpenClaw.

Usage:
    python3 setup_channel.py --name <channel-name> --context <context> [--category-id <category-id>] [--id <channel-id>]

Examples:
    python3 setup_channel.py --name fitness --context "Fitness tracking and workout planning"
    python3 setup_channel.py --name research --context "Deep research" --category-id "1234567890"
    python3 setup_channel.py --name personal-finance --id 1466184336901537897 --context "Personal finance"
"""

import argparse
import json
import sys
import os
from pathlib import Path
from typing import Optional, Dict, Any
from urllib.request import Request, urlopen
from urllib.error import HTTPError

# Configuration
CONFIG_FILE = Path.home() / ".openclaw" / "config.json"


def load_config() -> Dict[str, Any]:
    """Load OpenClaw configuration."""
    if not CONFIG_FILE.exists():
        print(f"❌ Config not found: {CONFIG_FILE}")
        sys.exit(1)
    
    with open(CONFIG_FILE, 'r') as f:
        return json.load(f)


def get_discord_info(config: Dict[str, Any]) -> tuple[str, str]:
    """Extract Discord bot token and guild ID from config."""
    try:
        [REDACTED_SECRET]'channels']['discord']['token']
        guild_id = list(config['channels']['discord']['guilds'].keys())[0]
        return token, guild_id
    except (KeyError, IndexError):
        print("❌ Discord configuration not found in config")
        sys.exit(1)


def channel_exists(token: str, guild_id: str, channel_name: str) -> Optional[str]:
    """Check if a channel with the given name already exists. Returns channel ID if found."""
    url = f"https://discord.com/api/v10/guilds/{guild_id}/channels"
    req = Request(url, headers={"Authorization": f"Bot {token}"})
    
    try:
        with urlopen(req) as response:
            channels = json.loads(response.read())
            for channel in channels:
                if channel.get('name') == channel_name and channel.get('type') == 0:  # Text channel
                    return channel['id']
    except HTTPError:
        return None
    
    return None


def create_discord_channel(token: str, guild_id: str, channel_name: str, category_id: Optional[str] = None) -> Optional[str]:
    """Create a new Discord text channel."""
    url = f"https://discord.com/api/v10/guilds/{guild_id}/channels"
    
    payload = {
        "name": channel_name,
        "type": 0,  # Text channel
    }
    
    if category_id:
        payload["parent_id"] = category_id
    
    req = Request(
        url,
        data=json.dumps(payload).encode('utf-8'),
        headers={
            "Authorization": f"Bot {token}",
            "Content-Type": "application/json"
        },
        method='POST'
    )
    
    try:
        with urlopen(req) as response:
            result = json.loads(response.read())
            return result['id']
    except HTTPError as e:
        error_body = e.read().decode('utf-8')
        print(f"❌ Failed to create channel: {e.code} - {error_body}")
        return None


def build_gateway_config(channel_id: str, guild_id: str, context: str) -> Dict[str, Any]:
    """Build gateway config patch for the channel."""
    return {
        "channels": {
            "discord": {
                "guilds": {
                    guild_id: {
                        "channels": {
                            channel_id: {
                                "allow": True,
                                "requireMention": False,
                                "systemPrompt": context
                            }
                        }
                    }
                }
            }
        }
    }


def main():
    parser = argparse.ArgumentParser(description="Setup Discord channel for OpenClaw")
    parser.add_argument("--name", required=True, help="Channel name (e.g., 'fitness', 'personal-finance')")
    parser.add_argument("--id", help="Channel ID if it already exists")
    parser.add_argument("--context", help="Channel context/purpose")
    parser.add_argument("--category-id", help="Discord category ID to place channel in (optional)")
    
    args = parser.parse_args()
    
    channel_name = args.name.lower()
    channel_id = args.id
    context = args.context
    category_id = args.category_id or os.environ.get("DISCORD_CATEGORY_ID")
    
    print(f"🔧 Setting up Discord channel: #{channel_name}")
    
    # Validate context is provided
    if not context:
        print("❌ Error: --context is required")
        print("   Specify the channel's purpose with --context \"Your description here\"")
        sys.exit(1)
    
    # Load config
    config = load_config()
    token, guild_id = get_discord_info(config)
    
    # Check if channel exists or create it
    if not channel_id:
        print(f"🔍 Checking if channel #{channel_name} exists...")
        channel_id = channel_exists(token, guild_id, channel_name)
        
        if channel_id:
            print(f"✅ Found existing channel: {channel_id}")
        else:
            print(f"📝 Channel doesn't exist. Creating it...")
            
            if category_id:
                print(f"   Using category ID: {category_id}")
            else:
                print(f"   Creating uncategorized channel (no category specified)")
            
            channel_id = create_discord_channel(token, guild_id, channel_name, category_id)
            if not channel_id:
                sys.exit(1)
            
            print(f"✅ Created channel #{channel_name} (ID: {channel_id})")
    
    # Build gateway config patch
    patch = build_gateway_config(channel_id, guild_id, context)
    
    print(f"\n✅ Channel #{channel_name} setup complete!")
    print(f"   Channel ID: {channel_id}")
    print(f"   Context: {context}")
    print(f"\n📝 Run this command to apply the gateway config:")
    print(f"\nopenclaw gateway config.patch --raw '{json.dumps(patch)}'")
    print(f"\n⚠️  Gateway will restart automatically after applying config.")


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)