Skill: zouxuyang/agent-manager-skill (version 1.0.0)

*** name: agent-manager description: Manage OpenClaw Agents. Create new agents, configure workspaces, manage Feishu bot integrations (QR code scanning, bindings), and verify multi-bot routing. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ # Agent 管理专家 (Agent Manager) 本技能用于帮助用户快速创建、配置和管理 OpenClaw Agent 及其飞书绑定。 ## 核心流程（必须严格遵守） ### 1. 需求确认 (Triage) - ⚠️ 关键原则：信息收集完才能发确认 在执行任何操作前，你需要收集齐以下所有信息： - Agent 的中文名称（如果用户说了主要用途或者功能描述，你可以帮忙拟名字） - Agent 的主要用途/功能描述（用户自己说，或者你根据用户提供的信息去补充） - 是否需要绑定飞书机器人（默认需要创建新的机器人） **收集规则：** 信息都收集齐全后，使用下面的模板向用户发送确认卡片。 **最终确认模板：** ``` 📋 创建计划确认 请确认以下信息： - **Agent 中文名**：[用户提供的名称] - **Agent ID**：[自动推断的英文ID，例如 sales-assistant] - **工作区路径**：`~/.openclaw/workspace-[ID]` - **功能描述**：[用户提供的功能描述] - **飞书机器人**：[新建（默认）/绑定已有/暂不绑定] - **机器人权限**：[全部放开（默认）/需要认证] 确认请回复"确认"或"是"，如有修改请说明。 ``` ### 2. 飞书快捷创建流程 (Feishu Quick Setup Flow) 如果用户选择了“新建”飞书机器人，**在确认计划后，你需要执行以下交互式步骤**： 1. **获取授权链接**：运行以下脚本生成飞书授权 URL： ```bash python3 ~/.openclaw/workspace/skills/agent-manager/scripts/feishu_auth.py generate ``` 2. **发送快捷授权卡片给用户**：脚本运行后会输出 `URL=https://...`。请**使用 Markdown 的链接语法包装成一个醒目的按钮/卡片形式**发送给用户，并提示他们点击授权。 **必须使用的回复格式示例：** > 请点击下方按钮完成飞书机器人授权： > > 🔗 **[👉 点击这里快捷创建飞书机器人 👈](这里填入脚本输出的URL)** > > *(授权完成后，请回复“好了”或“已完成”)* 3. **轮询结果**：当用户回复“好了”之后，运行轮询脚本获取 `app_id` 和 `app_secret`： ```bash python3 ~/.openclaw/workspace/skills/agent-manager/scripts/feishu_auth.py poll ``` ### 3. 执行创建 (Execution) 在完成确认（和飞书凭证获取）后，使用提供的脚本创建 Agent： ```bash # 如果不需要绑定飞书 python3 ~/.openclaw/workspace/skills/agent-manager/scripts/manage_agent.py create \ --name "销售助手" \ --id "sales-assistant" \ --description "负责处理销售数据和客户跟进" # 如果需要绑定飞书，且权限为【全部放开（默认，open）】 python3 ~/.openclaw/workspace/skills/agent-manager/scripts/manage_agent.py create \ --name "销售助手" \ --id "sales-assistant" \ --description "负责处理销售数据和客户跟进" \ --app-id "cli_xxx" \ --app-secret "yyy" # 如果需要绑定飞书，且用户明确要求【需要认证（pairing）】 python3 ~/.openclaw/workspace/skills/agent-manager/scripts/manage_agent.py create \ --name "销售助手" \ --id "sales-assistant" \ --description "负责处理销售数据和客户跟进" \ --app-id "cli_xxx" \ --app-secret "yyy" \ --require-pairing ``` ### 4. 收尾工作 (Cleanup & Restart) **必须按顺序严格执行以下步骤：** 1. **执行完毕后，先向用户汇报**：“✅ Agent [中文名] 及其相关配置已经创建完毕！为了避免 Git 嵌套冲突，我已经自动清理了它的 `.git` 目录。” 2. **询问是否重启**：向用户询问：“一切准备就绪，需要现在为您重启 OpenClaw 网关以使新机器人上线吗？” 3. **等待用户回复**：只有当用户明确回复“重启”、“好的”或类似同意的指令后。 4. **执行重启**： - 发消息：“🔄 正在重启网关，请稍候（约30秒）...” - 执行命令：`openclaw gateway restart` - 检查状态：`openclaw gateway status` - 发消息告知：“✅ 网关重启成功！[中文名]已上线，可以开始使用了。” ## 附带资源 (Bundled Resources) - `scripts/manage_agent.py`: 核心创建脚本（自动创建 Agent，写入配置并清理工作区）。 - `scripts/feishu_auth.py`: 飞书快捷授权链接生成与轮询脚本。 - `scripts/verify_bindings.py`: 验证飞书账号与 Agent 的绑定关系。

path: README.md
content: # Agent 管理专家 (Agent Manager Skill)

这是一个专为 OpenClaw 打造的核心技能（Skill），旨在赋予 AI 助手**自我繁衍和管理**的能力。通过这个技能，你可以直接在聊天框里用自然语言让 OpenClaw 帮你创建新的子 Agent、配置工作区，甚至全自动完成飞书机器人的注册与绑定。

## ✨ 核心特性

- **一句话建机器人**：只需告诉 AI “帮我建一个负责处理报销的助理”，它会自动推断 ID、撰写人设（SOUL.md）并初始化干净的独立工作区。
- **飞书无缝集成**：告别繁琐的开发者后台配置！直接在聊天框发送飞书原生交互卡片，一键唤起侧边栏授权，自动获取凭证并绑定。
- **安全的权限控制**：默认创建全员可用的飞书机器人，但你可以随时要求开启“身份认证（Pairing）”来保护私密 Agent。
- **优雅的防冲突机制**：自动清理新 Agent 工作区中可能导致 Git 嵌套冲突的冗余文件，并在重启网关前主动向你请示。

## 📂 目录结构

```text
agent-manager/
├── SKILL.md                  # 技能说明与提示词（大模型看的文档）
├── README.md                 # 技能介绍（你正在看的文档）
└── scripts/
    ├── manage_agent.py       # 核心调度脚本（创建Agent、写配置、清理工作区）
    ├── feishu_auth.py        # 飞书 OAuth 授权流与轮询脚本
    └── verify_bindings.py    # 飞书路由绑定关系检查脚本
```

## 🚀 如何使用？

无需任何前置配置。在安装了此技能的 OpenClaw 会话中，你可以直接对大模型说：

> *"我想建一个新助手，专门负责查股票，需要绑定飞书，并且仅我个人可用（需要认证）。"*

大模型会按照以下标准工作流响应你：

1. **信息确认**：向你发送一份详细的《创建计划确认》表单。
2. **飞书授权**（若需要）：给你发送一张精美的飞书交互卡片，点击即可在端内完成扫码授权。
3. **自动执行**：当你回复“好了”之后，它会在后台飞速建好 Agent、写好 JSON 配置，并清理环境。
4. **请示重启**：一切就绪后，它会询问你是否现在重启网关让新机器人上线。

## 🛠️ 手动调用脚本（高级用户）

如果你更喜欢在终端（Terminal）里操作，这些内置脚本也完全支持独立运行。

**创建一个不绑飞书的本地 Agent：**
```bash
python3 scripts/manage_agent.py create \
  --name "数据分析师" \
  --id "data-analyst" \
  --description "负责处理 Excel 和生成图表"
```

**检查当前的飞书路由绑定状态：**
```bash
python3 scripts/verify_bindings.py
```


path: scripts/manage_agent.py
content: #!/usr/bin/env python3
"""
OpenClaw Agent Management Script
Provides robust creation of agents and workspace initialization.
"""
import argparse
import subprocess
import os
from pathlib import Path
import json

def run_cmd(cmd, check=True):
    print(f"Executing: {' '.join(cmd)}")
    subprocess.run(cmd, check=check)

def create_agent(name, agent_id, description=""):
    workspace = f"{os.path.expanduser('~')}/.openclaw/workspace-{agent_id}"
    
    # 1. Create agent via CLI
    run_cmd(["openclaw", "agents", "add", agent_id, "--workspace", workspace])
    
    # 2. Set Identity
    run_cmd(["openclaw", "agents", "set-identity", "--agent", agent_id, "--name", name, "--json"])
    
    # 3. Initialize Standard Files
    ws_path = Path(workspace)
    os.makedirs(ws_path, exist_ok=True)
    
    # 4. Remove .git directory if it exists to prevent nested git repos
    git_dir = ws_path / ".git"
    if git_dir.exists() and git_dir.is_dir():
        import shutil
        shutil.rmtree(git_dir)
        print(f"Removed nested .git directory at {git_dir}")
        
    agents_md = f"""# {name} 指南

## 核心目标
{description or "协助用户完成特定任务。"}

## 记忆使用规范
*   **会话开始时**：读取 `USER.md` 和 `SOUL.md`。
*   **日常记录**：记录到 `memory/YYYY-MM-DD.md`。
*   **长期记忆**：重要信息整理至 `MEMORY.md`。
"""
    with open(ws_path / "AGENTS.md", "w") as f: f.write(agents_md)

    soul_md = f"""# {name} 的人设

## 角色定位
你是 {name}。
{description}

## 语气风格
* 专业、高效。

## 边界
* 不捏造事实，权限操作需确认。
"""
    with open(ws_path / "SOUL.md", "w") as f: f.write(soul_md)
    
    print(f"✅ Agent {agent_id} ({name}) created successfully at {workspace}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest="command", required=True)
    
    create_parser = subparsers.add_parser("create")
    create_parser.add_argument("--name", required=True)
    create_parser.add_argument("--id", required=True)
    create_parser.add_argument("--description", default="")
    create_parser.add_argument("--app-id", help="Feishu App ID")
    create_parser.add_argument("--app-secret", help="Feishu App Secret")
    create_parser.add_argument("--require-pairing", action="store_true", help="Set dmPolicy to pairing")
    
    args = parser.parse_args()
    if args.command == "create":
        create_agent(args.name, args.id, args.description)
        
        # If feishu credentials are provided, bind them
        if args.app_id and args.app_secret:
            config_path = f"{os.path.expanduser('~')}/.openclaw/openclaw.json"
            try:
                with open(config_path, 'r') as f:
                    config = json.load(f)
                
                account_id = f"{args.id}_bot"
                
                # Add account
                if 'channels' not in config: config['channels'] = {}
                if 'feishu' not in config['channels']: config['channels']['feishu'] = {}
                if 'accounts' not in config['channels']['feishu']: config['channels']['feishu']['accounts'] = {}
                if 'bindings' not in config['channels']['feishu']: config['channels']['feishu']['bindings'] = {}
                
                account_data = {
                    'appId': args.app_id,
                    'appSecret': args.app_secret,
                    'botName': args.name
                }
                
                if args.require_pairing:
                    account_data['dmPolicy'] = "pairing"
                    
                config['channels']['feishu']['accounts'][account_id] = account_data
                
                # Add binding
                config['channels']['feishu']['bindings'][account_id] = args.id
                
                with open(config_path, 'w') as f:
                    json.dump(config, f, indent=2, ensure_ascii=False)
                    
                print(f"✅ Feishu bot bound successfully to {args.id} as {account_id}")
            except Exception as e:
                print(f"❌ Error updating openclaw.json for Feishu: {e}")


path: scripts/verify_bindings.py
content: #!/usr/bin/env python3
"""
Verify Feishu bot bindings in openclaw.json
"""
import json

CONFIG_PATH = f"{os.path.expanduser('~')}/.openclaw/openclaw.json"

def verify_bindings():
    try:
        with open(CONFIG_PATH, 'r') as f:
            config = json.load(f)
        
        feishu_config = config.get('channels', {}).get('feishu', {})
        bindings = feishu_config.get('bindings', {})
        accounts = feishu_config.get('accounts', {})
        
        print("🔍 Current Feishu Bot Bindings:")
        print("-" * 50)
        
        if not bindings:
            print("No bindings found.")
            return

        for account_id, agent_id in bindings.items():
            app_id = accounts.get(account_id, {}).get('appId', '❌ Missing')
            print(f"Account: {account_id}")
            print(f"  └─ App ID: {app_id}")
            print(f"  └─ Agent:  {agent_id}")
            print()
            
    except Exception as e:
        print(f"Error reading config: {e}")

if __name__ == "__main__":
    verify_bindings()


path: scripts/feishu_auth.py
content: #!/usr/bin/env python3
"""
Feishu Authorization URL Generation and Polling Script
"""
import sys
import argparse
import requests
import json
import time
import os

DEVICE_CODE_FILE = "/tmp/feishu_device_code.txt"

def generate_url():
    try:
        resp = requests.post('https://accounts.feishu.cn/oauth/v1/app/registration',
            data={'action': 'begin', 'archetype': 'PersonalAgent', 'auth_method': 'client_secret', 'request_user_info': 'open_id'},
            headers={'Content-Type': 'application/x-www-form-urlencoded'})
        resp.raise_for_status()
        data = resp.json()
        
        device_code = data.get('device_code')
        user_code = data.get('user_code')
        
        if not device_code or not user_code:
            print("Error: Failed to get authorization codes.")
            return

        with open(DEVICE_CODE_FILE, "w") as f:
            f.write(device_code)

        url = f"https://open.feishu.cn/page/openclaw?user_code={user_code}"
        print(f"URL={url}")
        
    except Exception as e:
        print(f"Error generating URL: {e}")

def poll_result():
    if not os.path.exists(DEVICE_CODE_FILE):
        print("Error: No device code found. Please run 'generate' first.")
        return

    with open(DEVICE_CODE_FILE, "r") as f:
        device_code = f.read().strip()

    print("Polling Feishu for authorization result...")
    for i in range(20):
        try:
            resp = requests.post('https://accounts.feishu.cn/oauth/v1/app/registration',
                data={'action': 'poll', 'device_code': device_code},
                headers={'Content-Type': 'application/x-www-form-urlencoded'}, timeout=10)
            result = resp.json()
            
            if 'client_id' in result:
                app_id = result['client_id']
                app_secret = result['client_secret']
                print("\n✅ Authorization successful!")
                print(f"App ID: {app_id}")
                print(f"App [REDACTED_SECRET]")
                print("\nYou can now pass these credentials to the manage_agent.py script.")
                
                # Cleanup
                os.remove(DEVICE_CODE_FILE)
                if os.path.exists("/tmp/feishu_qr.png"):
                    os.remove("/tmp/feishu_qr.png")
                return
                
            print(f"Waiting... (Attempt {i+1}/20)")
            time.sleep(5)
        except Exception as e:
            print(f"Polling error: {e}")
            time.sleep(5)
            
    print("\n❌ Polling timeout. User may not have completed authorization.")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("action", choices=["generate", "poll"])
    args = parser.parse_args()

    if args.action == "generate":
        generate_url()
    elif args.action == "poll":
        poll_result()


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