Skill: absolins/openclaw-agent-feishu-onboarding (version 1.0.1)

--- name: openclaw-agent-feishu-onboarding description: Create OpenClaw agents and onboard Feishu routing with explicit multi-step confirmations. Use when the user needs to (1) define a new agent role and workspace, (2) collect and confirm Feishu route fields including peer.id, (3) apply account/peer bindings, and (4) validate routing and rollback safely. --- # OpenClaw Agent Feishu Onboarding Standardize creation of new OpenClaw agents and Feishu route binding. Use this skill for operational execution with strict confirmations. ## Runtime Prerequisites - Required binaries: - `openclaw` (CLI available in PATH) - `python` (3.x, used by `scripts/validate_feishu_bindings.py`) - Preflight checks: - `openclaw --version` - `python --version` - Data access behavior: - Reads local OpenClaw config (`openclaw.json` / `OPENCLAW_CONFIG_PATH`) - May write local agent and routing state (`agents.list[]`, `bindings[]`) - Do not run on machines where local config access is not permitted ## Core Scope - Create a new agent (`agents.list` + workspace + identity). - Confirm Feishu route target fields before any write. - Bind routing using `bindings.match` with `channel/accountId/peer`. - Verify final route and provide rollback steps. ## Required Inputs - Agent intent: - Agent purpose and responsibility boundary. - Explicit out-of-scope items. - Agent spec: - `agentId` (lowercase, digits, hyphen). - `workspace` path. - `model` id. - Identity object in `agents.list[]`: - use `identity` object (for example `identity.name`, `identity.emoji`, `identity.theme`) - do not rely on top-level `name` only - Feishu route spec: - `match.channel`: `feishu` - `match.accountId`: for example `main` - `match.peer.kind`: `group` (or `dm` when needed) - `match.peer.id`: Feishu session id (group id like `oc_xxx` or dm id) ## Hard Validation Gates (Must Pass) - `agents.list[]` entry for new agent must include `identity` object. - `identity.name` must be set for human-readable routing/debug checks. - `match.accountId` must be one of `channels.feishu.accounts` keys (for example `main`). - Never put Feishu group/session id (`oc_xxx`) into `match.accountId`. - Group routing must include `match.peer = { kind: "group", id: "oc_xxx" }`. - If routing is for a specific group but `match.peer` is missing, abort and correct config before continuing. - Before write, print the final binding object and require explicit confirmation. ## Multi-Step Confirmation Protocol Do not skip confirmations. Ask and confirm in this exact order. 1. Confirmation A: Agent Goal - Confirm what this agent should do. - Confirm what this agent must not do. 2. Confirmation B: Agent Configuration - Confirm `agentId`, `workspace`, model, identity fields. - Confirm naming conventions and collision check (`agentId` uniqueness). 3. Confirmation C: Feishu Routing Target - Confirm `accountId`. - Confirm `peer.kind`. - Confirm `peer.id` (explicitly state this is the Feishu session id). - Confirm whether this is a precise peer binding or account-level fallback. 4. Confirmation D: Execution Approval - Summarize all fields in one compact block. - Ask for final go/no-go before writing config. ## Execution Workflow 1. Discover current state: - `openclaw agents list` - `openclaw config get agents.list --json` - `openclaw config get channels.feishu.accounts --json` - `openclaw directory groups list --channel feishu --account <account-id> --query "<keyword>" --json` - `openclaw config get bindings --json` 2. Create agent: - `openclaw agents add ...` - Optional: `openclaw agents set-identity ...` 3. Apply routing: - Use `openclaw agents bind` for account-scoped binding when needed. - Ensure peer-precise rule exists in top-level `bindings[]` with `match.peer`. 4. Validate: - `python -X utf8 ./scripts/validate_feishu_bindings.py --config <openclaw.json-path>` - `openclaw config validate --json` - ensure target `agents.list[]` entry has `identity.name` - Check `bindings[]` entry content and ordering. - Ensure every group-targeted rule has `match.peer.kind = "group"` and `match.peer.id = "oc_xxx"`. 5. Reload/restart gateway if required by deployment policy. ## Routing Rules - `peer` rule is the precise route key for a specific Feishu conversation. - Use `match.peer.id` to bind a specific session (group `oc_xxx`). - If both peer-level and account-level bindings exist, keep peer rule first. - Avoid broad fallback rules until peer-specific routes are confirmed. - Wrong example (forbidden): `match.accountId: "oc_xxx"` without `match.peer`. Agent list entry should follow this shape: ```json { "id": "data-analyst", "workspace": "C:\\Users\\Administrator\\.openclaw\\workspace-dataAnalysis", "agentDir": "C:\\Users\\Administrator\\.openclaw\\agents\\data-analyst\\agent", "identity": { "name": "data-analyst" } } ``` Canonical peer binding object: ```json { "agentId": "<agent-id>", "match": { "channel": "feishu", "accountId": "main", "peer": { "kind": "group", "id": "oc_xxx" } } } ``` ## Safety and Rollback - Never delete agents unless explicitly requested. - Before changing `bindings`, capture current `bindings` snapshot. - If wrong route is applied, revert to previous binding set and re-validate. - Prefer reversible, explicit changes and post-change verification. ## Why It Still Routes to `main` - Binding did not match the real incoming `peer.id` (most common). - `bindings[]` was written but gateway runtime did not reload the latest config. - `match.accountId` mismatched current Feishu account key. - Target agent exists but `agents.list[]` entry is malformed (missing required fields such as `identity` object expected by your convention). - Message arrived in a different conversation than the configured group. ## References - Command examples and rollback snippets: [commands.md](./references/commands.md) - Chinese runbook and confirmation checklist: [usage-zh.md](./references/usage-zh.md) - Auto-check script: [validate_feishu_bindings.py](./scripts/validate_feishu_bindings.py)

path: scripts/validate_feishu_bindings.py
content: #!/usr/bin/env python3
"""
Validate Feishu routing bindings in an OpenClaw config file.

Default behavior is strict for this skill:
- every Feishu route binding must include match.peer.kind and match.peer.id
- accountId must be a configured Feishu account key (e.g. "main")
- accountId must never look like a Feishu group/session id (oc_xxx)
"""

from __future__ import annotations

import argparse
import json
import os
import pathlib
import sys
from typing import Any


def _load_json(path: pathlib.Path) -> dict[str, Any]:
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except FileNotFoundError as exc:
        raise SystemExit(f"[ERROR] config not found: {path}") from exc
    except json.JSONDecodeError as exc:
        raise SystemExit(f"[ERROR] invalid JSON at {path}: {exc}") from exc


def _resolve_config_path(user_path: str | None) -> pathlib.Path:
    if user_path:
        return pathlib.Path(user_path).expanduser().resolve()
    env_path = os.environ.get("OPENCLAW_CONFIG_PATH")
    if env_path:
        return pathlib.Path(env_path).expanduser().resolve()
    return pathlib.Path.home().joinpath(".openclaw", "openclaw.json").resolve()


def _extract_feishu_accounts(cfg: dict[str, Any]) -> set[str]:
    accounts = (
        cfg.get("channels", {})
        .get("feishu", {})
        .get("accounts", {})
    )
    if not isinstance(accounts, dict):
        return set()
    # "default" is a pointer, not a real account definition.
    return {k for k, v in accounts.items() if k != "default" and isinstance(v, dict)}


def _validate_binding(
    binding: dict[str, Any],
    index: int,
    feishu_accounts: set[str],
    allow_account_only: bool,
) -> list[str]:
    errors: list[str] = []
    match = binding.get("match")
    if not isinstance(match, dict):
        return errors

    if match.get("channel") != "feishu":
        return errors

    account_id = match.get("accountId")
    if not isinstance(account_id, str) or not account_id.strip():
        errors.append(f"bindings[{index}]: missing or invalid match.accountId")
    else:
        if account_id.startswith("oc_"):
            errors.append(
                f'bindings[{index}]: match.accountId="{account_id}" looks like a Feishu group id; '
                "use account key (e.g. main) and put group id in match.peer.id"
            )
        if feishu_accounts and account_id not in feishu_accounts and account_id != "*":
            errors.append(
                f'bindings[{index}]: match.accountId="{account_id}" is not in channels.feishu.accounts '
                f"({sorted(feishu_accounts)})"
            )

    peer = match.get("peer")
    if peer is None:
        if not allow_account_only:
            errors.append(f"bindings[{index}]: missing match.peer (peer structure is required)")
        return errors

    if not isinstance(peer, dict):
        errors.append(f"bindings[{index}]: match.peer must be an object")
        return errors

    peer_kind = peer.get("kind")
    peer_id = peer.get("id")
    if not isinstance(peer_kind, str) or not peer_kind.strip():
        errors.append(f"bindings[{index}]: missing or invalid match.peer.kind")
    if not isinstance(peer_id, str) or not peer_id.strip():
        errors.append(f"bindings[{index}]: missing or invalid match.peer.id")

    if isinstance(peer_id, str) and peer_id.startswith("oc_") and peer_kind != "group":
        errors.append(
            f'bindings[{index}]: match.peer.id="{peer_id}" looks like Feishu group id; '
            'match.peer.kind should be "group"'
        )
    if peer_kind == "group" and isinstance(peer_id, str) and not peer_id.startswith("oc_"):
        errors.append(
            f'bindings[{index}]: match.peer.kind="group" should use group id like "oc_xxx", got "{peer_id}"'
        )

    return errors


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Validate Feishu bindings in openclaw.json"
    )
    parser.add_argument(
        "--config",
        help="Path to openclaw.json (default: OPENCLAW_CONFIG_PATH or ~/.openclaw/openclaw.json)",
    )
    parser.add_argument(
        "--allow-account-only",
        action="store_true",
        help="Allow Feishu bindings without match.peer (account-level fallback).",
    )
    args = parser.parse_args()

    cfg_path = _resolve_config_path(args.config)
    cfg = _load_json(cfg_path)

    bindings = cfg.get("bindings", [])
    if not isinstance(bindings, list):
        print("[ERROR] top-level bindings must be an array", file=sys.stderr)
        return 2

    feishu_accounts = _extract_feishu_accounts(cfg)
    all_errors: list[str] = []
    feishu_binding_count = 0

    for i, binding in enumerate(bindings):
        if not isinstance(binding, dict):
            continue
        match = binding.get("match")
        if isinstance(match, dict) and match.get("channel") == "feishu":
            feishu_binding_count += 1
        all_errors.extend(
            _validate_binding(
                binding=binding,
                index=i,
                feishu_accounts=feishu_accounts,
                allow_account_only=args.allow_account_only,
            )
        )

    if all_errors:
        print(f"[FAIL] Found {len(all_errors)} issue(s) in Feishu bindings:")
        for msg in all_errors:
            print(f"- {msg}")
        return 1

    print(
        "[OK] Feishu bindings are valid "
        f"(checked {feishu_binding_count} binding(s), config={cfg_path})"
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


path: references/commands.md
content: # OpenClaw Agent + Feishu Routing Commands

## 1) Preflight Discovery
```bash
openclaw --version
python --version
openclaw agents list
openclaw config get agents.list --json
openclaw config get channels.feishu.accounts --json
openclaw directory groups list --channel feishu --account <account-id> --query "<group-keyword>" --json
openclaw config get bindings --json
```

Note:
- This workflow reads and updates local `openclaw.json` state.

## 2) Create Agent
```bash
openclaw agents add <agent-id> \
  --non-interactive \
  --workspace <workspace-dir> \
  --model <provider/model-id> \
  --json
```

Example:
```bash
openclaw agents add product-ops \
  --non-interactive \
  --workspace C:\Users\Administrator\.openclaw\workspace-product-ops \
  --model minimax-portal/MiniMax-M2.5 \
  --json
```

## 3) Set Identity (recommended)
```bash
openclaw agents set-identity \
  --agent <agent-id> \
  --name "<display-name>" \
  --emoji "🦞" \
  --json
```

Identity convention check (important):
- Ensure `agents.list[]` uses `identity` object, for example:
```json
{
  "id": "data-analyst",
  "identity": {
    "name": "data-analyst"
  }
}
```

## 4) Optional Account-Level Binding
```bash
openclaw agents bind --agent <agent-id> --bind feishu:<account-id> --json
```

## 5) Peer-Level Binding (Feishu session id)
When exact routing is required, ensure a `bindings[]` entry like this exists:

```json
{
  "agentId": "<agent-id>",
  "match": {
    "channel": "feishu",
    "accountId": "<account-id>",
    "peer": {
      "kind": "group",
      "id": "oc_xxx"
    }
  }
}
```

Notes:
- `peer.id` is the Feishu session id (group id is typically `oc_xxx`).
- Keep peer-specific rules before broad account/channel fallback rules.
- `accountId` must be account key like `main`, never `oc_xxx`.

Wrong (do not use):

```json
{
  "agentId": "data-analyst",
  "match": {
    "channel": "feishu",
    "accountId": "oc_1cf2f6cc799743acd65159968bd1afb5"
  }
}
```

Right:

```json
{
  "agentId": "data-analyst",
  "match": {
    "channel": "feishu",
    "accountId": "main",
    "peer": {
      "kind": "group",
      "id": "oc_1cf2f6cc799743acd65159968bd1afb5"
    }
  }
}
```

## 6) Validate
```bash
python -X utf8 .\scripts\validate_feishu_bindings.py --config C:\Users\Administrator\.openclaw\openclaw.json
openclaw config validate --json
openclaw config get agents.list --json
openclaw config get bindings --json
```

## 7) Why messages still go to main
- Check if the actual incoming group id equals configured `match.peer.id`.
- Check `match.accountId` is the real Feishu account key (`main` in your config).
- Restart/reload gateway after config change if routing cache/runtime has not picked latest config.
- Check target agent exists in `agents.list[]` and has `identity.name`.

## 8) Rollback
Rollback by restoring previous `bindings` snapshot and re-run validation:

```bash
openclaw config validate --json
```

If account-level route needs cleanup:
```bash
openclaw agents unbind --agent <agent-id> --bind feishu:<account-id> --json
```


path: references/usage-zh.md
content: # OpenClaw Agent 创建与飞书路由绑定（中文流程）

## 1. 目标

本技能只做两件事：
- 创建 OpenClaw Agent。
- 将 Agent 绑定到飞书路由（重点是 `bindings.match.peer.id`）。

其中 `peer.id` 就是飞书会话 ID：
- 群聊通常是 `oc_xxx`
- 私聊是对应用户会话 ID

## 1.1 运行前提与权限说明
- 必须具备本地命令：
  - `openclaw`
  - `python`（3.x）
- 建议先检查：
```bash
openclaw --version
python --version
```
- 本技能会读取并可能写入本地 `openclaw.json`（如 `agents.list[]`、`bindings[]`）。
- `openclaw.json` 可能包含敏感配置（凭据等），仅在受信任环境执行。

## 2. 必填信息

### A. Agent 信息
- Agent 职责边界：做什么、不做什么
- `agentId`
- `workspace`
- `model`
- `agents.list[]` 中必须使用 `identity` 对象（参考当前配置写法）

示例（推荐）：
```json
{
  "id": "data-analyst",
  "workspace": "C:\\Users\\Administrator\\.openclaw\\workspace-dataAnalysis",
  "agentDir": "C:\\Users\\Administrator\\.openclaw\\agents\\data-analyst\\agent",
  "identity": {
    "name": "data-analyst"
  }
}
```

### B. 飞书路由信息
- `match.channel = feishu`
- `match.accountId`（例如 `main`）
- `match.peer.kind`（常见 `group`）
- `match.peer.id`（飞书会话 ID）

强约束：
- `match.accountId` 只能是飞书账号键（例如 `main`）。
- `oc_xxx` 这类群会话 ID 只能放在 `match.peer.id`，不能放在 `match.accountId`。

## 3. 四步确认机制（必须按顺序）

### 确认 1：确认要创建什么 Agent
确认项：
- 目标职责
- 输出风格
- 禁止范围

输出模板：
```text
Agent 目标确认：
- 角色：
- 主要职责：
- 不做事项：
请确认是否正确（是/否）。
```

### 确认 2：确认 Agent 配置参数
确认项：
- `agentId`
- `workspace`
- `model`
- `name/emoji/theme`（如有）

输出模板：
```text
Agent 配置确认：
- agentId:
- workspace:
- model:
- identity(name/emoji/theme):
请确认是否正确（是/否）。
```

### 确认 3：确认飞书路由参数
确认项：
- `accountId`
- `peer.kind`
- `peer.id`（明确说明这是飞书会话 ID）

输出模板：
```text
飞书路由确认：
- channel: feishu
- accountId:
- peer.kind:
- peer.id:
请确认是否正确（是/否）。
```

### 确认 4：最终执行确认
在真正写入前做一次总确认。

输出模板：
```text
最终执行确认：
将创建 Agent 并写入路由绑定，摘要如下：
- agentId:
- workspace:
- model:
- route: feishu/<accountId>/<peer.kind>/<peer.id>
请确认执行（执行/取消）。
```

## 4. 执行流程（SOP）

1. 读取现状
```bash
openclaw agents list
openclaw config get agents.list --json
openclaw config get channels.feishu.accounts --json
openclaw directory groups list --channel feishu --account <account-id> --query "<群关键词>" --json
openclaw config get bindings --json
```

2. 创建 Agent
```bash
openclaw agents add <agent-id> \
  --non-interactive \
  --workspace <workspace-dir> \
  --model <provider/model-id> \
  --json
```

3. 设置身份（推荐）
```bash
openclaw agents set-identity --agent <agent-id> --name "<display-name>" --emoji "🦞" --json
```
说明：你的规范是 `agents.list[]` 里使用 `identity` 对象，不要只写顶层 `name`。

4. 绑定路由
- 账户级（可选）：
```bash
openclaw agents bind --agent <agent-id> --bind feishu:<account-id> --json
```
- 会话级（重点）：在 `bindings[]` 中写入 `match.peer.id = <飞书会话ID>`

示例：
```json
{
  "agentId": "product-ops",
  "match": {
    "channel": "feishu",
    "accountId": "main",
    "peer": { "kind": "group", "id": "oc_xxx" }
  }
}
```

反例（错误）：
```json
{
  "agentId": "product-ops",
  "match": {
    "channel": "feishu",
    "accountId": "oc_xxx"
  }
}
```

5. 验收
```bash
python -X utf8 .\scripts\validate_feishu_bindings.py --config C:\Users\Administrator\.openclaw\openclaw.json
openclaw config validate --json
openclaw config get agents.list --json
openclaw config get bindings --json
```

验收标准：
- 新 Agent 已存在
- 对应 `peer.id` 的绑定项存在且字段准确
- 无 schema 校验错误
- 自动检查脚本返回 `[OK]`

## 5. 排障与回滚

### 常见问题
- 绑定到了错误群：检查 `peer.id` 是否填错
- `accountId` 写成了 `oc_xxx`：这是字段混淆，必须改为账号键（如 `main`），并把 `oc_xxx` 放入 `peer.id`
- 命中了错误 Agent：检查 `bindings` 顺序（精确规则应优先）
- 配置不生效：先 `config validate` 再按你部署策略重载网关
- 群里仍走 `main`：通常是当前消息的真实 `peer.id` 与配置不一致，或网关尚未加载最新配置
- Agent 已创建但显示/路由异常：检查 `agents.list[]` 是否包含 `identity.name`

### 回滚原则
- 变更前先保存 `bindings` 快照
- 回滚时恢复上一个可用 `bindings`，再次校验

## 6. 操作纪律

- 没有完成 4 次确认，不执行写操作
- 每次写操作后必须做读回校验
- 统一保留 JSON 变更记录，便于审计与交接


path: agents/openai.yaml
content: interface:
  display_name: "Agent + Feishu Onboarding"
  short_description: "Create agents and bind Feishu peer routes; performs local openclaw.json reads/writes"
  default_prompt: "Use $openclaw-agent-feishu-onboarding to create a new agent and bind it to a specific Feishu peer.id after step-by-step confirmation."
policy:
  allow_implicit_invocation: false


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