fix(plugins): refresh bundled channel skills (#119020)

This commit is contained in:
Patrick Erichsen
2026-08-03 17:31:00 -07:00
committed by GitHub
parent 07a9856cce
commit 1aea2df935
13 changed files with 216 additions and 815 deletions

View File

@@ -1,136 +1,43 @@
---
name: discord
description: "Discord message-tool ops: send/read/edit/delete, react, poll, pin, thread, search, presence, media/components."
description: "Discord messaging workflows through OpenClaw's message tool."
metadata: { "openclaw": { "emoji": "🎮", "requires": { "config": ["channels.discord.token"] } } }
allowed-tools: ["message"]
---
# Discord
Use the `message` tool with `channel: "discord"`. No separate Discord tool.
Use the `message` tool with `channel: "discord"`. The tool schema lists the actions enabled by the current account's `channels.discord.actions.*` gates; do not assume unavailable actions.
## Rules
## Workflow
- Respect `channels.discord.actions.*` gates.
- Prefer explicit `guildId`, `channelId`, `messageId`, `userId`.
- Multi-account: pass `accountId` when needed.
- Send targets: `to: "channel:<id>"` or `to: "user:<id>"`.
- Mention users as `<@USER_ID>`.
- Avoid Markdown tables in outbound Discord messages.
- Prefer components v2 for rich UI; do not mix v2 `components` with legacy `embeds`.
- Prefer stable `guildId`, `channelId`, `messageId`, and `userId` values from context. Pass `accountId` when more than one Discord account could apply.
- Resolve the exact message before editing, deleting, pinning, moderating, or reacting when the user's reference is ambiguous.
- Keep thread replies in their existing thread. A forum parent cannot receive components; send components to the created forum thread instead.
- Confirm destructive or moderation actions unless the user already specified the exact target and action.
## Common actions
## Interactive components
Send:
```json
{ "action": "send", "channel": "discord", "to": "channel:123", "message": "hello", "silent": true }
```
Send media:
`components` must be a structured object or native component array, never a placeholder string. Do not combine components v2 with legacy `embeds`.
```json
{
"action": "send",
"channel": "discord",
"to": "channel:123",
"message": "see attachment",
"media": "file:///tmp/example.png"
"message": "Choose an option",
"components": {
"blocks": [
{
"type": "actions",
"buttons": [
{ "label": "Approve", "style": "success", "callbackData": "approve" },
{ "label": "Decline", "style": "danger", "callbackData": "decline" }
]
}
]
}
}
```
Components v2:
```json
{
"action": "send",
"channel": "discord",
"to": "channel:123",
"message": "Status",
"components": "[Carbon v2 components]"
}
```
React:
```json
{ "action": "react", "channel": "discord", "channelId": "123", "messageId": "456", "emoji": "👍" }
```
Read:
```json
{ "action": "read", "channel": "discord", "to": "channel:123", "limit": 20 }
```
Edit/delete:
```json
{
"action": "edit",
"channel": "discord",
"channelId": "123",
"messageId": "456",
"message": "fixed typo"
}
```
```json
{ "action": "delete", "channel": "discord", "channelId": "123", "messageId": "456" }
```
Poll:
```json
{
"action": "poll",
"channel": "discord",
"to": "channel:123",
"pollQuestion": "Lunch?",
"pollOption": ["Pizza", "Sushi"],
"pollDurationHours": 24
}
```
Pin:
```json
{ "action": "pin", "channel": "discord", "channelId": "123", "messageId": "456" }
```
Thread:
```json
{
"action": "thread-create",
"channel": "discord",
"channelId": "123",
"messageId": "456",
"threadName": "bug triage"
}
```
Search:
```json
{
"action": "search",
"channel": "discord",
"guildId": "999",
"query": "release notes",
"channelIds": ["123"],
"limit": 10
}
```
Presence, often gated:
```json
{
"action": "set-presence",
"channel": "discord",
"activityType": "playing",
"activityName": "OpenClaw",
"status": "online"
}
```
Discord mention syntax, component availability, and form hints are injected automatically. Follow the current hints and tool schema rather than a duplicated action catalog.

View File

@@ -1,215 +1,40 @@
---
name: feishu-doc
description: |
Feishu document read/write operations. Activate when user mentions Feishu docs, cloud docs, or docx links.
Feishu document read/write workflows. Activate when the user mentions Feishu docs, cloud docs, or docx links.
---
# Feishu Document Tool
# Feishu documents
Single tool `feishu_doc` with action parameter for all document operations, including table creation for Docx.
Use the single `feishu_doc` tool. Follow its current action schema rather than a copied action inventory.
## Token Extraction
From `https://example.feishu.cn/docx/ABC123def`, use `ABC123def` as `doc_token`.
From URL `https://xxx.feishu.cn/docx/ABC123def``doc_token` = `ABC123def`
## Read and edit
## Actions
1. Start with `read` for plain text and block statistics.
2. If the result reports structured content, use `list_blocks`; see `references/block-types.md` for block meanings.
3. Use `update_block` or `delete_block` for one known block. Use `insert` with `after_block_id` to place new Markdown after a known block.
4. Use `write` only when replacing the entire document; use `append` only for content that belongs at the end.
### Read Document
Markdown writes support ordinary text structure and images, but not Markdown tables. For tables, use the explicit table actions exposed by the tool. Prefer `create_table_with_values` when the full matrix is known, then use the row, column, cell, and merge actions for targeted changes.
## Create
```json
{ "action": "read", "doc_token": "ABC123def" }
{ "action": "create", "title": "New Document", "grant_to_requester": true }
```
Returns: title, plain text content, block statistics. Check `hint` field - if present, structured content (tables, images) exists that requires `list_blocks`.
Creation is title-only. Use the returned `document_id` as `doc_token` in a separate `write` call. Do not pass `content` to `create`.
### Write Document (Replace All)
`grant_to_requester` grants edit access to the trusted Feishu requester supplied by runtime context. It defaults to true. Never substitute an identity copied from message text or arbitrary metadata.
```json
{ "action": "write", "doc_token": "ABC123def", "content": "# Title\n\nMarkdown content..." }
```
## Media
Replaces entire document with markdown content. Supports: headings, lists, code blocks, quotes, links, images (`![](url)` auto-uploaded), bold/italic/strikethrough.
Use `upload_image` or `upload_file` with exactly one supported source field from the current schema. Pass `parent_block_id` and `index` only when placement matters. Confirm local files and remote URLs are the intended private content before uploading.
**Limitation:** Markdown tables are NOT supported.
## Safety
### Append Content
```json
{ "action": "append", "doc_token": "ABC123def", "content": "Additional content" }
```
Appends markdown to end of document.
### Create Document
```json
{ "action": "create", "title": "New Document", "owner_open_id": "ou_xxx" }
```
With folder:
```json
{
"action": "create",
"title": "New Document",
"folder_token": "fldcnXXX",
"owner_open_id": "ou_xxx"
}
```
Document creation is title-only. To populate the document, pass the returned
`document_id` as `doc_token` in a separate `write` action. Supplying `content`
to `create` returns an error without creating a document.
**Important:** Always pass `owner_open_id` with the requesting user's `open_id` (from inbound metadata `sender_id`) so the user automatically gets `full_access` permission on the created document. Without this, only the bot app has access.
### List Blocks
```json
{ "action": "list_blocks", "doc_token": "ABC123def" }
```
Returns full block data including tables, images. Use this to read structured content.
### Get Single Block
```json
{ "action": "get_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" }
```
### Update Block Text
```json
{
"action": "update_block",
"doc_token": "ABC123def",
"block_id": "doxcnXXX",
"content": "New text"
}
```
### Delete Block
```json
{ "action": "delete_block", "doc_token": "ABC123def", "block_id": "doxcnXXX" }
```
### Create Table (Docx Table Block)
```json
{
"action": "create_table",
"doc_token": "ABC123def",
"row_size": 2,
"column_size": 2,
"column_width": [200, 200]
}
```
Optional: `parent_block_id` to insert under a specific block.
### Write Table Cells
```json
{
"action": "write_table_cells",
"doc_token": "ABC123def",
"table_block_id": "doxcnTABLE",
"values": [
["A1", "B1"],
["A2", "B2"]
]
}
```
### Create Table With Values (One-step)
```json
{
"action": "create_table_with_values",
"doc_token": "ABC123def",
"row_size": 2,
"column_size": 2,
"column_width": [200, 200],
"values": [
["A1", "B1"],
["A2", "B2"]
]
}
```
Optional: `parent_block_id` to insert under a specific block.
### Upload Image to Docx (from URL or local file)
```json
{
"action": "upload_image",
"doc_token": "ABC123def",
"url": "https://example.com/image.png"
}
```
Or local path with position control:
```json
{
"action": "upload_image",
"doc_token": "ABC123def",
"file_path": "/tmp/image.png",
"parent_block_id": "doxcnParent",
"index": 5
}
```
Optional `index` (0-based) inserts the image at a specific position among sibling blocks. Omit to append at end.
**Note:** Image display size is determined by the uploaded image's pixel dimensions. For small images (e.g. 480x270 GIFs), scale to 800px+ width before uploading to ensure proper display.
### Upload File Attachment to Docx (from URL or local file)
```json
{
"action": "upload_file",
"doc_token": "ABC123def",
"url": "https://example.com/report.pdf"
}
```
Or local path:
```json
{
"action": "upload_file",
"doc_token": "ABC123def",
"file_path": "/tmp/report.pdf",
"filename": "Q1-report.pdf"
}
```
Rules:
- exactly one of `url` / `file_path`
- optional `filename` override
- optional `parent_block_id`
## Reading Workflow
1. Start with `action: "read"` - get plain text + statistics
2. Check `block_types` in response for Table, Image, Code, etc.
3. If structured content exists, use `action: "list_blocks"` for full data
## Configuration
```yaml
channels:
feishu:
tools:
doc: true # default: true
```
**Note:** `feishu_wiki` depends on this tool - wiki page content is read/written via `feishu_doc`.
## Permissions
Required: `docx:document`, `docx:document:readonly`, `docx:document.block:convert`, `drive:drive`
- Resolve exact document and block IDs before destructive edits.
- Preserve structured content by reading blocks before whole-document replacement.
- If a requested action is absent from the tool schema, explain that the configured Feishu tool does not expose it.

View File

@@ -1,6 +1,6 @@
# Feishu Block Types Reference
Complete reference for Feishu document block types. Use with `feishu_doc_list_blocks`, `feishu_doc_update_block`, and `feishu_doc_delete_block`.
Complete reference for Feishu document block types. Use the single `feishu_doc` tool with its `list_blocks`, `update_block`, and `delete_block` actions.
## Block Type Table
@@ -59,10 +59,11 @@ Complete reference for Feishu document block types. Use with `feishu_doc_list_bl
### Text-based blocks (2-17, 19)
Update text content using `feishu_doc_update_block`:
Update text content with `feishu_doc`:
```json
{
"action": "update_block",
"doc_token": "ABC123",
"block_id": "block_xxx",
"content": "New text content"
@@ -71,13 +72,11 @@ Update text content using `feishu_doc_update_block`:
### Image blocks (27)
Images cannot be updated directly via `update_block`. Use `feishu_doc_write` or `feishu_doc_append` with markdown to add new images.
Images cannot be updated directly via `update_block`. Use `upload_image` to add a replacement image, then delete the old block only after the new upload succeeds.
### Table blocks (31)
**Important:** Table blocks CANNOT be created via the `documentBlockChildren.create` API (error 1770029). This affects `feishu_doc_write` and `feishu_doc_append` - markdown tables will be skipped with a warning.
Tables can only be read (via `list_blocks`) and individual cells (type 32) can be updated, but new tables cannot be inserted programmatically via markdown.
Markdown tables are not converted by `write`, `append`, or `insert`. Create tables with `create_table` or `create_table_with_values`; use the table row, column, cell, and merge actions exposed by the current schema for later edits.
### Container blocks (24, 25, 35)
@@ -87,17 +86,13 @@ Grid and QuoteContainer are layout containers. Edit their child blocks instead.
### Replace specific paragraph
1. `feishu_doc_list_blocks` - find the block_id
2. `feishu_doc_update_block` - update its content
1. Call `feishu_doc` with `action: "list_blocks"` to find the block ID.
2. Call `feishu_doc` with `action: "update_block"` to replace its text.
### Insert content at specific location
Currently, the API only supports appending to document end. For insertion at specific positions, consider:
1. Read existing content
2. Delete affected blocks
3. Rewrite with new content in desired order
Call `feishu_doc` with `action: "insert"`, the target `doc_token`, Markdown `content`, and the preceding block's ID as `after_block_id`.
### Delete multiple blocks
Blocks must be deleted one at a time. Delete child blocks before parent containers.
Call `delete_block` for exact block IDs. Delete child blocks before parent containers and confirm broad deletions first.

View File

@@ -1,114 +1,28 @@
---
name: feishu-drive
description: |
Feishu cloud storage file management. Activate when user mentions cloud space, folders, drive.
Feishu cloud-storage and comment workflows. Activate when the user mentions cloud space, folders, Drive files, or document comments.
---
# Feishu Drive Tool
# Feishu Drive
Single tool `feishu_drive` for cloud storage operations.
Use the single `feishu_drive` tool and its current action schema.
## Token Extraction
From `https://example.feishu.cn/drive/folder/ABC123`, use `ABC123` as `folder_token`.
From URL `https://xxx.feishu.cn/drive/folder/ABC123``folder_token` = `ABC123`
## Files and folders
## Actions
- Start from a folder shared with the bot. Bot credentials normally have no usable personal root folder.
- For paginated folder listings, keep the same `folder_token` and pass the returned `page_token` until no continuation token remains.
- Use `info` with the exact file token and type returned by Drive or wiki discovery.
- Resolve the exact source and destination before moving or deleting. Confirm destructive deletes when the user's intent or target is unclear.
- Create subfolders inside a shared folder; creating at the account root normally fails for bots.
### List Folder Contents
## Comments
```json
{ "action": "list" }
```
- Use `list_comments`, then `list_comment_replies` with the exact `comment_id` to inspect a discussion.
- Use `add_comment` for a document-level comment. Include `block_id` only when the user wants a comment on one known Docx block.
- Use `reply_comment` for an existing comment thread.
- Preserve the file type and pagination fields returned by the tool. The schema is authoritative for which file types each comment action accepts.
Requests the account root (no `folder_token`). Bot credentials normally have no root folder, so
use a folder that has been shared with the bot instead.
```json
{ "action": "list", "folder_token": "fldcnXXX", "page_size": 100 }
```
Returns one page of files with token, name, type, url, timestamps, and `next_page_token` when
another page is available. To continue, pass the returned token with the same folder token:
```json
{
"action": "list",
"folder_token": "fldcnXXX",
"page_size": 100,
"page_token": "next-page-token"
}
```
`page_size` must be between 1 and 200. Pagination requires a concrete shared `folder_token`;
root-list cursors are not forwarded.
### Get File Info
```json
{ "action": "info", "file_token": "ABC123", "type": "docx" }
```
Looks up file metadata directly by token and type, regardless of which shared folder contains it.
Shortcuts are the exception: Feishu's metadata API does not support the `shortcut` type, so shortcut
info retains the root-directory lookup behavior.
`type`: `doc`, `docx`, `sheet`, `bitable`, `folder`, `file`, `mindnote`, `shortcut`
### Create Folder
```json
{ "action": "create_folder", "name": "New Folder" }
```
In parent folder:
```json
{ "action": "create_folder", "name": "New Folder", "folder_token": "fldcnXXX" }
```
### Move File
```json
{ "action": "move", "file_token": "ABC123", "type": "docx", "folder_token": "fldcnXXX" }
```
### Delete File
```json
{ "action": "delete", "file_token": "ABC123", "type": "docx" }
```
## File Types
| Type | Description |
| ---------- | ----------------------- |
| `doc` | Old format document |
| `docx` | New format document |
| `sheet` | Spreadsheet |
| `bitable` | Multi-dimensional table |
| `folder` | Folder |
| `file` | Uploaded file |
| `mindnote` | Mind map |
| `shortcut` | Shortcut |
## Configuration
```yaml
channels:
feishu:
tools:
drive: true # default: true
```
## Permissions
- `drive:drive` - Full access (create, move, delete)
- `drive:drive:readonly` - Read only (list and root-level info fallback)
- `drive:drive.metadata:readonly` - Direct `info` lookup outside the root (not needed with `drive:drive`)
## Known Limitations
- **Bots have no root folder**: Feishu bots use `tenant_access_token` and don't have their own "My Space". The root folder concept only exists for user accounts. This means:
- `create_folder` without `folder_token` will fail (400 error)
- Bot can only access files/folders that have been **shared with it**
- **Workaround**: User must first create a folder manually and share it with the bot, then bot can create subfolders inside it
Only expose or forward file and comment contents needed for the user's request; shared Drive data may be private.

View File

@@ -1,119 +1,19 @@
---
name: feishu-perm
description: |
Feishu permission management for documents and files. Activate when user mentions sharing, permissions, collaborators.
Feishu collaborator and permission workflows. Activate when the user explicitly asks to inspect or change sharing, permissions, or collaborators.
---
# Feishu Permission Tool
# Feishu permissions
Single tool `feishu_perm` for managing file/document permissions.
Use the single `feishu_perm` tool and its current action schema. This tool is disabled by default because it changes access to user data; if it is unavailable, explain that `channels.feishu.tools.perm` must be enabled.
## Actions
## Workflow
### List Collaborators
1. Resolve the exact file token and type.
2. Use `list` to inspect current collaborators before changing access.
3. For `add`, resolve the collaborator's exact identifier and choose the least permission that satisfies the request.
4. For `remove`, confirm the exact collaborator and file when the request is ambiguous or broad.
5. Report the resulting permission change without exposing unrelated collaborator data.
```json
{ "action": "list", "token": "ABC123", "type": "docx" }
```
Returns: members with member_type, member_id, perm, name.
### Add Collaborator
```json
{
"action": "add",
"token": "ABC123",
"type": "docx",
"member_type": "email",
"member_id": "user@example.com",
"perm": "edit"
}
```
### Remove Collaborator
```json
{
"action": "remove",
"token": "ABC123",
"type": "docx",
"member_type": "email",
"member_id": "user@example.com"
}
```
## Token Types
| Type | Description |
| ---------- | ----------------------- |
| `doc` | Old format document |
| `docx` | New format document |
| `sheet` | Spreadsheet |
| `bitable` | Multi-dimensional table |
| `folder` | Folder |
| `file` | Uploaded file |
| `wiki` | Wiki node |
| `mindnote` | Mind map |
## Member Types
| Type | Description |
| ------------------ | ------------------ |
| `email` | Email address |
| `openid` | User open_id |
| `userid` | User user_id |
| `unionid` | User union_id |
| `openchat` | Group chat open_id |
| `opendepartmentid` | Department open_id |
## Permission Levels
| Perm | Description |
| ------------- | ------------------------------------ |
| `view` | View only |
| `edit` | Can edit |
| `full_access` | Full access (can manage permissions) |
## Examples
Share document with email:
```json
{
"action": "add",
"token": "doxcnXXX",
"type": "docx",
"member_type": "email",
"member_id": "alice@company.com",
"perm": "edit"
}
```
Share folder with group:
```json
{
"action": "add",
"token": "fldcnXXX",
"type": "folder",
"member_type": "openchat",
"member_id": "oc_xxx",
"perm": "view"
}
```
## Configuration
```yaml
channels:
feishu:
tools:
perm: true # default: false (disabled)
```
**Note:** This tool is disabled by default because permission management is a sensitive operation. Enable explicitly if needed.
## Permissions
Required: `drive:permission`
Never infer an email, user ID, department, or chat from a display name alone. Follow the current schema for supported member types, token types, and permission levels.

View File

@@ -1,122 +1,26 @@
---
name: feishu-wiki
description: |
Feishu knowledge base navigation. Activate when user mentions knowledge base, wiki, or wiki links.
Feishu knowledge-base navigation workflows. Activate when the user mentions a knowledge base, wiki, or wiki link.
---
# Feishu Wiki Tool
# Feishu wiki
Single tool `feishu_wiki` for knowledge base operations.
Use the single `feishu_wiki` tool and its current action schema.
Wiki `space_id` values are opaque strings. Always keep them quoted in tool calls, even when they contain only digits; passing a long numeric-looking ID as a number can corrupt the suffix due to JavaScript number precision limits.
From `https://example.feishu.cn/wiki/ABC123def`, use `ABC123def` as `token`. Treat every `space_id` as an opaque quoted string, even when it contains only digits.
## Token Extraction
## Navigate
From URL `https://xxx.feishu.cn/wiki/ABC123def``token` = `ABC123def`
- Use `spaces` to enumerate accessible knowledge spaces and `nodes` for a space or parent node.
- Continue pagination with the returned `page_token` while `has_more` is true, keeping the same space and parent.
- Use `search` when the user provides a query but not an exact node.
- Use `get` to resolve a wiki token to its `node_token`, `obj_token`, and `obj_type`.
## Actions
## Create and organize
### List Knowledge Spaces
Wiki creation supports only `docx`, `sheet`, and `bitable`; `docx` is the default. Resolve an exact space and parent before create, move, or rename operations, and confirm ambiguous or destructive reorganizations.
```json
{ "action": "spaces" }
```
## Wiki content workflow
Returns one page of accessible wiki spaces plus `has_more` and `page_token`.
Continue with the returned `page_token` while `has_more` is true:
```json
{ "action": "spaces", "page_token": "next-page-token" }
```
### List Nodes
```json
{ "action": "nodes", "space_id": "7xxx" }
```
With parent:
```json
{ "action": "nodes", "space_id": "7xxx", "parent_node_token": "wikcnXXX" }
```
Returns one page of nodes plus `has_more` and `page_token`. Continue with the
same `space_id` and `parent_node_token`, adding the returned `page_token`, while
`has_more` is true. Both list actions accept optional `page_size` from 1 to 50.
### Get Node Details
```json
{ "action": "get", "token": "ABC123def" }
```
Returns: `node_token`, `obj_token`, `obj_type`, etc. Use `obj_token` with `feishu_doc` to read/write the document.
### Create Node
```json
{ "action": "create", "space_id": "7xxx", "title": "New Page" }
```
With type and parent:
```json
{
"action": "create",
"space_id": "7xxx",
"title": "Sheet",
"obj_type": "sheet",
"parent_node_token": "wikcnXXX"
}
```
`obj_type`: `docx` (default), `sheet`, `bitable`, `mindnote`, `file`, `doc`, `slides`
### Move Node
```json
{ "action": "move", "space_id": "7xxx", "node_token": "wikcnXXX" }
```
To different location:
```json
{
"action": "move",
"space_id": "7xxx",
"node_token": "wikcnXXX",
"target_space_id": "7yyy",
"target_parent_token": "wikcnYYY"
}
```
### Rename Node
```json
{ "action": "rename", "space_id": "7xxx", "node_token": "wikcnXXX", "title": "New Title" }
```
## Wiki-Doc Workflow
To edit a wiki page:
1. Get node: `{ "action": "get", "token": "wiki_token" }` → returns `obj_token`
2. Read doc: `feishu_doc { "action": "read", "doc_token": "obj_token" }`
3. Write doc: `feishu_doc { "action": "write", "doc_token": "obj_token", "content": "..." }`
## Configuration
```yaml
channels:
feishu:
tools:
wiki: true # default: true
doc: true # required - wiki content uses feishu_doc
```
**Dependency:** This tool requires `feishu_doc` to be enabled. Wiki pages are documents - use `feishu_wiki` to navigate, then `feishu_doc` to read/edit content.
## Permissions
Required: `wiki:wiki` or `wiki:wiki:readonly`
Wiki navigation is independent of the document tool. When `get` returns a `docx` object, use its `obj_token` as `doc_token` with `feishu_doc` to read or edit the page. Other object types require a currently available tool that supports that type; do not treat them as documents.

View File

@@ -25,7 +25,7 @@ metadata:
# imsg
Use `imsg` when an OpenClaw agent must act through the user's local macOS Messages.app account: inspect iMessage/SMS history, choose the correct DM or group, send messages/files, reply, react, vote in polls, or use private-API iMessage features.
Use the generic `message` tool first for actions that its current iMessage schema exposes. Use `imsg` when the task needs local Messages.app history, target discovery, watching, or an administrative/private-API capability that is not exposed by `message`.
Do not use this skill for Telegram, Signal, WhatsApp, Discord, Slack, or for replying inside the current OpenClaw conversation when the configured channel already routes the reply.
@@ -33,7 +33,7 @@ Do not use this skill for Telegram, Signal, WhatsApp, Discord, Slack, or for rep
1. Resolve the conversation first.
2. Choose DM, existing group, or new group.
3. Pick the lowest-capability command that can do the requested action.
3. Prefer an available `message` action; otherwise pick the lowest-capability `imsg` command that preserves the requested semantics.
4. Confirm any send or visible state change unless the user already gave exact recipient, content, and action.
5. Execute with stable identifiers: prefer `--chat-id` for normal sends/watch/history and `--chat` chat GUID for bridge actions.
@@ -74,7 +74,7 @@ Do not make `jq` a hard prerequisite for the skill; it is only a convenient form
## Capability Choice
Use standard commands for reads, target resolution, and plain sends:
Use `imsg` standard commands when the current `message` schema does not cover the operation or when local history is needed:
- Read/list/search/watch: `chats`, `group`, `history`, `search`, `watch`
- Basic text/file send: `send`

View File

@@ -36,81 +36,7 @@ metadata: { "openclaw": { "emoji": "⏰", "requires": { "config": ["channels.qqb
1. 调用 `qqbot_remind``{ "action": "add", "content": "喝水", "time": "5m" }`
2. 工具返回成功后,回复用户:`⏰ 好的5分钟后提醒你喝水~`
---
## 备用方案(直接使用 `cron` 工具)
> 仅当 `qqbot_remind` 工具不可用但 `cron` 工具可用时使用以下方式。
### 核心规则
> **payload.kind 必须是 `"agentTurn"`,绝对不能用 `"systemEvent"`**
> `systemEvent` 只在 AI 会话内部注入文本,用户收不到 QQ 消息。
**不可更改字段**
| 字段 | 固定值 | 原因 |
| -------------------- | ------------- | ---------------------------- |
| `payload.kind` | `"agentTurn"` | `systemEvent` 不会发 QQ 消息 |
| `delivery.mode` | `"announce"` | 主动投递模式 |
| `delivery.channel` | `"qqbot"` | QQ 通道标识 |
| `delivery.to` | 目标地址 | 从当前会话上下文获取 |
| `delivery.accountId` | 当前账户 ID | 多账号场景下不可省略 |
| `sessionTarget` | `"isolated"` | 隔离会话避免污染 |
> `schedule.at` 必须是**绝对 ISO 8601 时间戳**(如 `"2026-07-29T09:00:00.000Z"`),不支持 `"5m"` 等相对字符串。
> 计算方式:`当前时间戳ms + 延迟毫秒`。
### 一次性提醒schedule.kind = "at"
```json
{
"action": "add",
"job": {
"name": "{任务名}",
"schedule": { "kind": "at", "at": "{当前时间戳ms + N*60000 转换为 ISO 8601}" },
"sessionTarget": "isolated",
"wakeMode": "now",
"deleteAfterRun": true,
"payload": {
"kind": "agentTurn",
"message": "你是一个暖心的提醒助手。请用温暖、有趣的方式提醒用户:{提醒内容}。要求:(1) 不要回复HEARTBEAT_OK (2) 不要解释你是谁 (3) 直接输出一条暖心的提醒消息 (4) 可以加一句简短的鸡汤或关怀的话 (5) 控制在2-3句话以内 (6) 用emoji点缀"
},
"delivery": {
"mode": "announce",
"channel": "qqbot",
"to": "qqbot:c2c:{openid}",
"accountId": "{accountId}"
}
}
}
```
### 周期提醒schedule.kind = "cron"
```json
{
"action": "add",
"job": {
"name": "{任务名}",
"schedule": { "kind": "cron", "expr": "0 8 * * *", "tz": "{用户确认的 IANA 时区}" },
"sessionTarget": "isolated",
"wakeMode": "now",
"payload": {
"kind": "agentTurn",
"message": "你是一个暖心的提醒助手。请用温暖、有趣的方式提醒用户:{提醒内容}。要求:(1) 不要回复HEARTBEAT_OK (2) 不要解释你是谁 (3) 直接输出一条暖心的提醒消息 (4) 可以加一句简短的鸡汤或关怀的话 (5) 控制在2-3句话以内 (6) 用emoji点缀"
},
"delivery": {
"mode": "announce",
"channel": "qqbot",
"to": "qqbot:c2c:{openid}",
"accountId": "{accountId}"
}
}
}
```
> 周期任务**不加** `deleteAfterRun`。群聊 `delivery.to` 格式为 `"qqbot:group:{group_openid}"`。
`qqbot_remind` 不可用时,不要绕过它直接创建 Gateway 任务。说明当前无法安全注册 QQ 提醒,并建议用户检查 QQBot 工具配置。
---

View File

@@ -1,78 +1,20 @@
---
name: slack
description: "Slack tool actions: send/read/edit/delete messages, react, pin/unpin, list pins/reactions/emoji, member info."
description: "Slack messaging workflows through OpenClaw's message tool."
metadata: { "openclaw": { "emoji": "💬", "requires": { "config": ["channels.slack"] } } }
allowed-tools: ["message"]
---
# Slack
Use the `slack` tool. Reuse `channelId` and Slack timestamp message IDs from context when present.
Use the `message` tool with `channel: "slack"`. The tool schema lists the actions enabled for the current Slack account; do not assume an action that is not present.
## Inputs
## Workflow
- `channelId`: Slack channel ID.
- `messageId`: Slack timestamp, e.g. `1712023032.1234`.
- `to`: `channel:<id>` or `user:<id>` for sends.
- `emoji`: Unicode or `:name:` for reactions.
- Prefer stable Slack IDs from context. Sends outside the current conversation use `channel:<id>` or `user:<id>` targets.
- Keep replies in the current thread unless the user asks for a top-level post. For another thread, pass its Slack timestamp as `threadId`; use the same timestamp as `messageId` for message-specific actions.
- Before editing, deleting, pinning, or reacting to an ambiguous message, read the conversation and resolve its exact ID.
- When multiple Slack accounts are configured, pass `accountId` rather than guessing.
- Confirm destructive deletes when the target or intent is unclear.
## Actions
```json
{ "action": "sendMessage", "to": "channel:C123", "content": "Hello" }
```
```json
{ "action": "readMessages", "channelId": "C123", "limit": 20 }
```
```json
{
"action": "react",
"channelId": "C123",
"messageId": "1712023032.1234",
"emoji": ":white_check_mark:"
}
```
```json
{ "action": "reactions", "channelId": "C123", "messageId": "1712023032.1234" }
```
```json
{
"action": "editMessage",
"channelId": "C123",
"messageId": "1712023032.1234",
"content": "Updated text"
}
```
```json
{ "action": "deleteMessage", "channelId": "C123", "messageId": "1712023032.1234" }
```
```json
{ "action": "pinMessage", "channelId": "C123", "messageId": "1712023032.1234" }
```
```json
{ "action": "unpinMessage", "channelId": "C123", "messageId": "1712023032.1234" }
```
```json
{ "action": "listPins", "channelId": "C123" }
```
```json
{ "action": "memberInfo", "userId": "U123" }
```
```json
{ "action": "emojiList" }
```
## Safety
- Confirm destructive deletes when context is unclear.
- Keep outbound messages short; avoid Markdown tables.
- Prefer thread/message IDs over fuzzy channel names.
Slack formatting, mentions, tables, charts, and interactive controls are described automatically by the current `message` tool hints. Follow those hints instead of maintaining a second action or formatting catalog here.

View File

@@ -10,7 +10,7 @@
"dependencies": {
"@aws-sdk/client-s3": "3.1095.0",
"@aws-sdk/s3-request-presigner": "3.1095.0",
"@tloncorp/tlon-skill": "0.4.4",
"@tloncorp/tlon-skill": "0.4.5",
"@urbit/aura": "3.0.0",
"zod": "4.4.3"
},

View File

@@ -20,7 +20,7 @@ metadata:
{
"id": "go",
"kind": "go",
"module": "github.com/steipete/wacli/cmd/wacli@latest",
"module": "github.com/openclaw/wacli/cmd/wacli@latest",
"bins": ["wacli"],
"label": "Install wacli (go)",
},

42
pnpm-lock.yaml generated
View File

@@ -1907,8 +1907,8 @@ importers:
specifier: 3.1095.0
version: 3.1095.0
'@tloncorp/tlon-skill':
specifier: 0.4.4
version: 0.4.4
specifier: 0.4.5
version: 0.4.5
'@urbit/aura':
specifier: 3.0.0
version: 3.0.0
@@ -5226,32 +5226,32 @@ packages:
resolution: {integrity: sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==}
engines: {node: '>=12.17.0'}
'@tloncorp/tlon-skill-darwin-arm64@0.4.4':
resolution: {integrity: sha512-yr7Lg4KJPWInBFarXhJV+Z8LxFFLGJAgoBY6d6sc7zVW40oKUM+OQjKomKaNy8H4aYk0CnZkXlN+CTOn3/LD6g==}
'@tloncorp/tlon-skill-darwin-arm64@0.4.5':
resolution: {integrity: sha512-urM3HnsoQR/yqT8qxs4DZu/B6bWS0EdNKcJXme5FdTsuX4NfOhbMAdjl+aasu2G86GF3q/fgH00g75DNiIFB9w==}
cpu: [arm64]
os: [darwin]
hasBin: true
'@tloncorp/tlon-skill-darwin-x64@0.4.4':
resolution: {integrity: sha512-h9yL4fwkLa2zXqROWV6QuqVB4FlQOA9iQfahST15MAjyQRBqJEt2zxjuXHbkvlZ9SKm4vS5J7YLXI6yXLvULGA==}
'@tloncorp/tlon-skill-darwin-x64@0.4.5':
resolution: {integrity: sha512-w+pr1NwiD6524WAIHwpQ0IjtqV1K7SUw2aqaG27lvluPukzolYWl3fcliJzY2AOeCwe2r3JkCq5JD1oiZGkLjQ==}
cpu: [x64]
os: [darwin]
hasBin: true
'@tloncorp/tlon-skill-linux-arm64@0.4.4':
resolution: {integrity: sha512-OBzY/ACHo9/wecN0XTkepK2mGwDnqcs+T7vEhjHPKr4aD+YGBvdgA0abeXcSsz9EXKVnRnaDMo9QDYcROy4DIw==}
'@tloncorp/tlon-skill-linux-arm64@0.4.5':
resolution: {integrity: sha512-t/N4nUF2abzQA22ONbjSdOEyQ0k6nWaA/wZ3OZ0Gzmsfa4caEbMwiHS/nY9po9p3Gb8IFXW7ZTMwMe7w2E3XlA==}
cpu: [arm64]
os: [linux]
hasBin: true
'@tloncorp/tlon-skill-linux-x64@0.4.4':
resolution: {integrity: sha512-KCSfVFDBvR/oaDoa/C4Zyvw8eZTq0GDEcaWfn0tbLF3oWDNziSG1a/VFsQzDhHQWI4n0oq2402/HEbMMAI0Fvw==}
'@tloncorp/tlon-skill-linux-x64@0.4.5':
resolution: {integrity: sha512-1u5cdmnjSeYGhgTSMTDrpenPOxCu7B5Q62cuviBeYaS7lvvNTNwynIwuO9QR3Q98r7rMMyzWExhDHJ+sRlSbsw==}
cpu: [x64]
os: [linux]
hasBin: true
'@tloncorp/tlon-skill@0.4.4':
resolution: {integrity: sha512-kjFEqZNieQZR7aSgdM54GRWfMYtukuZlMzlW6ThNWKwrX2YDl8ieOZ7dA0gGcp2TpPHbtTpj83Ro1DMPlcGZ5A==}
'@tloncorp/tlon-skill@0.4.5':
resolution: {integrity: sha512-rsQ4Qqm1oCGLz7rW/TFyVc+8KANifGNrY8jTEu4AbIgFfGR9m3I8tdRjSdAkVW7ZCZ7VDI/RlZhCN8s/dNz+Kw==}
hasBin: true
'@tokenizer/inflate@0.4.1':
@@ -12387,24 +12387,24 @@ snapshots:
'@tinyhttp/content-disposition@2.2.4':
optional: true
'@tloncorp/tlon-skill-darwin-arm64@0.4.4':
'@tloncorp/tlon-skill-darwin-arm64@0.4.5':
optional: true
'@tloncorp/tlon-skill-darwin-x64@0.4.4':
'@tloncorp/tlon-skill-darwin-x64@0.4.5':
optional: true
'@tloncorp/tlon-skill-linux-arm64@0.4.4':
'@tloncorp/tlon-skill-linux-arm64@0.4.5':
optional: true
'@tloncorp/tlon-skill-linux-x64@0.4.4':
'@tloncorp/tlon-skill-linux-x64@0.4.5':
optional: true
'@tloncorp/tlon-skill@0.4.4':
'@tloncorp/tlon-skill@0.4.5':
optionalDependencies:
'@tloncorp/tlon-skill-darwin-arm64': 0.4.4
'@tloncorp/tlon-skill-darwin-x64': 0.4.4
'@tloncorp/tlon-skill-linux-arm64': 0.4.4
'@tloncorp/tlon-skill-linux-x64': 0.4.4
'@tloncorp/tlon-skill-darwin-arm64': 0.4.5
'@tloncorp/tlon-skill-darwin-x64': 0.4.5
'@tloncorp/tlon-skill-linux-arm64': 0.4.5
'@tloncorp/tlon-skill-linux-x64': 0.4.5
'@tokenizer/inflate@0.4.1(supports-color@10.2.2)':
dependencies:

View File

@@ -0,0 +1,88 @@
import { readFileSync } from "node:fs";
import { dirname, relative, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { listGitTrackedFiles } from "../../test-utils/repo-files.js";
type PluginManifest = {
channels?: unknown;
skills?: unknown;
};
const RETIRED_SKILL_PATTERNS = [
{
pattern: /use\s+the\s+[`'"]slack[`'"]\s+tool/iu,
replacement: "the `message` tool",
},
{
pattern:
/["']action["']\s*:\s*["'](?:sendMessage|readMessages|editMessage|deleteMessage|pinMessage|unpinMessage|listPins|memberInfo|emojiList)["']/u,
replacement: "a canonical `message` action",
},
{ pattern: /owner_open_id/u, replacement: "grant_to_requester" },
{ pattern: /feishu_doc_list_blocks/u, replacement: "feishu_doc action=list_blocks" },
{ pattern: /feishu_doc_update_block/u, replacement: "feishu_doc action=update_block" },
{ pattern: /feishu_doc_delete_block/u, replacement: "feishu_doc action=delete_block" },
{
pattern: /["']components["']\s*:\s*["']\[Carbon v2 components\]["']/u,
replacement: "structured components",
},
{ pattern: /## 使 `cron` /u, replacement: "qqbot_remind only" },
{
pattern: /github\.com\/steipete\/wacli\/cmd\/wacli@latest/u,
replacement: "github.com/openclaw/wacli/cmd/wacli@latest",
},
] as const;
function listRepositoryOwnedChannelSkillFiles(): string[] {
const trackedFiles = listGitTrackedFiles({ pathspecs: ["extensions"] }) ?? [];
const trackedFileSet = new Set(trackedFiles);
const skillFiles = new Set<string>();
for (const manifestPath of trackedFiles.filter((file) =>
/^extensions\/[^/]+\/openclaw\.plugin\.json$/u.test(file),
)) {
const manifest = JSON.parse(
readFileSync(resolve(process.cwd(), manifestPath), "utf8"),
) as PluginManifest;
if (!Array.isArray(manifest.channels) || !Array.isArray(manifest.skills)) {
continue;
}
const pluginDir = dirname(manifestPath);
for (const skillRoot of manifest.skills) {
if (typeof skillRoot !== "string" || skillRoot.includes("node_modules")) {
continue;
}
const relativeRoot = relative(process.cwd(), resolve(pluginDir, skillRoot)).replaceAll(
"\\",
"/",
);
for (const file of trackedFileSet) {
if (file.startsWith(`${relativeRoot}/`) && file.endsWith(".md")) {
skillFiles.add(file);
}
}
}
}
return [...skillFiles].toSorted();
}
describe("bundled channel-provider skill contracts", () => {
it("does not teach retired tool, action, parameter, or install contracts", () => {
const failures: string[] = [];
const skillFiles = listRepositoryOwnedChannelSkillFiles();
expect(skillFiles.length).toBeGreaterThan(0);
for (const file of skillFiles) {
const source = readFileSync(resolve(process.cwd(), file), "utf8");
for (const entry of RETIRED_SKILL_PATTERNS) {
if (entry.pattern.test(source)) {
failures.push(`${file}: replace ${entry.pattern.source} with ${entry.replacement}`);
}
}
}
expect(failures).toStrictEqual([]);
});
});