* feat: skills i18n 改造 — schemaVersion 1.1,零向后兼容
把 21 个 skills + 1 个 agent + manifest/categories 全量迁移到 schemaVersion 1.1
的 i18n 结构,配套 CI AI 翻译流水线(GitHub Models)与本地工具链。
## 关键变更
### 数据结构(破坏性,schemaVersion 1.0 → 1.1)
- SKILL.md: 顶层 name 改为 ASCII slug(== 目录名,符合 agentskills.io 规范);
中文显示名/short_desc/description 全部迁入 metadata.i18n.<locale>
- agents/<id>/agent.json: shortDesc/fullDesc/tags/persona.{role,traits} 迁入
i18n.<locale>;changelog[].changes 改为 { <locale>: string[] } 对象
- categories.json: 每个分类的 label/description 迁入 i18n.<locale>,顶层只剩
color/icon
- manifest.json: 加 supportedLocales / defaultLocale;顶层 description 迁入
i18n.<locale>
### Body 文件结构
- 根 SKILL.md = frontmatter + default_locale (en-US) body
- SKILL.<locale>.md = 各 locale 的 markdown body(首行 <!-- locale: xx --> 自校验)
### 工具链(scripts/i18n/)
- glossary.json: zh→en 术语表 + do_not_translate 白名单
- schema/skill-frontmatter.schema.json: i18n frontmatter JSON Schema
- validate-i18n.py: 8 条校验规则(name 合规 / locale 完整性 / hash 一致性等)
- translate.py: GitHub Models / Anthropic 双 backend,sha256 增量翻译
- migrate.py: 一次性迁移脚本(旧格式 → i18n 结构)
### CI(.github/workflows/)
- i18n-validate.yml: PR 触发跑 validate + translate --check
- i18n-translate.yml: PR 触发用 GitHub Models(默认 openai/gpt-5-mini)翻译缺失
locale,自动追加 commit;可切到 ANTHROPIC_API_KEY 走 Claude
### 文档
- docs/I18N.md: 作者贡献指南(schema 说明 / 提交流程 / 常见问题)
- README.md: 加多语言段落
## 验证
- uv run scripts/i18n/validate-i18n.py: OK,49 文件 0 错误
- uv run scripts/i18n/translate.py --check: 0 stale locale
- 21 skills 标题数 zh-CN == en-US 严格对齐(最大 66=66)
- skills-ref 规范校验:全部通过(顶层 name ASCII slug + description 单字段)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(i18n): 修复 PR #1 review 反馈的 6 项问题
- schema: translated_by 正则放宽为 ^(human|ai:[A-Za-z0-9._:/-]+)$,接受
'ai:github:openai/gpt-5-mini' 这类 backend:model 形式(CI 翻译输出格式)
- README + docs/I18N.md: 修正"CI 用 Claude API"误导描述,正确说明默认是
GitHub Models(openai/gpt-5-mini)+ GITHUB_TOKEN,可选切到 Anthropic
- skills/minimax-tts/SKILL.md & SKILL.zh-CN.md: 删除多余的 ``` 闭合,避免
Markdown 后续渲染错乱
- skills/docx/SKILL.md: 翻译时丢失的 • Unicode escape 示例已恢复,
与 zh-CN 版本对齐
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
17 KiB
name, description, version, type, risk_level, status, disable-model-invocation, tags, metadata, market
| name | description | version | type | risk_level | status | disable-model-invocation | tags | metadata | market | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| mail-operations | Use this skill whenever the user wants to interact with email. This includes reading inbox, sending emails, replying, searching messages, managing labels and categories, downloading attachments, setting up auto-reply rules, or triggering agents to handle incoming emails. Supports Gmail, Outlook, and IMAP/SMTP (QQ Mail, 163, Yahoo, etc.) through DesireCore's local REST API. Use when 用户提到 邮件、邮箱、收件箱、发邮件、回复邮件、查邮件、Gmail、 Outlook、QQ邮箱、163邮箱、附件、标签、草稿、自动回复、邮件规则、 转发、抄送、未读邮件、收信、发信、邮件同步、邮件搜索。 | 1.0.2 | procedural | medium | enabled | true |
|
|
|
mail-operations Skill
L0: One-line Summary
Send and receive email, search, manage labels, and run automation rules via a local REST API; supports Gmail / Outlook / IMAP.
L1: Overview and Use Cases
Capability
mail-operations is a Procedural Skill that operates email systems through DesireCore's local REST API. It supports three mailbox types—Gmail (OAuth2), Outlook (MSAL), and IMAP/SMTP (QQ, 163, Yahoo, etc.)—covering send/receive, search, label management, attachment download, draft management, and automation rules.
Use Cases
- The user wants to view the inbox, send, or reply to email
- The user wants to search for specific emails or manage email labels/categories
- The user wants to download attachments or manage drafts
- The user wants to set up auto-reply rules or trigger an Agent to handle email
Core Value
- Unified interface: three mailbox types are operated through a unified API, lowering complexity
- Local and secure: all operations go through the local API; no need to expose credentials
- Smart integration: supports automation rules and Agent-based email handling
L2: Detailed Specification
API Basics
- Base URL:
https://127.0.0.1:62000/api - Authentication: none required (local service)
- Content-Type:
application/json - SSL: use
curl -kto skip self-signed certificate verification - Response format: success
{"code": 1, "msg": "Success", "result": ...}, failure{"code": 0, "msg": "error message"}
Mandatory Behavior Rules
The following rules have the highest priority and must be obeyed on every operation.
Rule 1: Operate Only Through the Local API
All email operations must and may only be done through https://127.0.0.1:62000. Never call an external email client or browser.
If the API returns 401 (authorization expired):
- Tell the user the account's authorization has expired
- Prompt the user to re-authorize in DesireCore
- Gmail:
POST /api/gmail/auth/initiate?loginHint={email}, Outlook:POST /api/outlook/auth/initiate
Rule 2: Confirm the Account Before Operating
Before performing any operation, you must first call GET /api/accounts to obtain the account list:
- Match the user's specified account by email address or domain ("QQ mailbox" → IMAP
qq.com) - Only one account exists and the user says "my mailbox" → use it directly
- No matching account found → tell the user and prompt them to add one
Rule 3: Auto-Sync When the Query Is Empty
When a query returns an empty list or cannot find the specified data:
- Call
POST /{provider}/messages/fetchto sync remote data - Automatically retry the original query
- Only inform the user if it is still empty
Rule 4: Prompt for a Refresh After Write Operations
After a write operation (send, reply, delete, mark, label change, etc.) succeeds, prompt: Operation completed. Do you want me to refresh the current mailbox view to see the latest state?
Quick Reference: Differences Between the Three Mailbox Types
| Feature | Gmail | Outlook | IMAP |
|---|---|---|---|
| Authorization | OAuth2 | OAuth2 (MSAL) | password / app password |
| Provider path | /gmail/ |
/outlook/ |
/imap/ |
| Message detail | path param /{id} |
query param ?id={id} |
UID /{uid}?folder= |
| Search | supported | not supported | not supported |
| Drafts | supported (incl. list/detail) | supported (create/send) | supported (create/send) |
| Attachment download | supported | supported | supported |
| Labels/categories | native labels | Categories | local labels only |
| Auto-rules | supported | supported | supported |
Core Operations
In the following, {p} denotes the provider (gmail, outlook, imap); {email} must be URL-encoded.
1. Account Management
| Operation | Method | Endpoint |
|---|---|---|
| Get all accounts | GET | /accounts |
| Get accounts with settings | GET | /accounts-with-settings |
| Delete account | DELETE | /accounts/{p}/{email} |
| Get account settings | GET | /accounts/{p}/{email}/settings |
| Update account settings | PUT | /accounts/{p}/{email}/settings |
| Update display name | PUT | /accounts/{p}/{email}/displayName — body: {"displayName": "..."} |
IMAP-specific:
| Operation | Method | Endpoint |
|---|---|---|
| Get preset mailbox configs | GET | /imap/presets — returns server configs for QQ/163/Yahoo, etc. |
| Test IMAP connection | POST | /imap/test — body: {email, password, imap: {host, port, secure}, smtp: {host, port, secure}} |
| Add IMAP account | POST | /imap/accounts — same body as above plus displayName |
2. Message List and Sync
| Operation | Method | Endpoint | Parameters |
|---|---|---|---|
| Query local cache | GET | /{p}/messages |
email, offset, limit, folder |
| Remote sync | POST | /{p}/messages/fetch |
email, limit, folder |
| Manually trigger sync | POST | /sync |
provider, email |
folder values: Gmail/Outlook: inbox, sent, drafts, trash, spam, archive; IMAP: INBOX, Sent, Drafts, Trash, etc.
Response format (message list item):
{
"id": "messageID", "subject": "subject",
"from": {"name": "...", "address": "..."},
"to": [{"name": "...", "address": "..."}],
"date": "ISO8601", "snippet": "snippet",
"isRead": true, "hasAttachments": false,
"labels": ["INBOX"], "folder": "inbox"
}
3. Single Message Operations
| Operation | Gmail | Outlook | IMAP |
|---|---|---|---|
| Get detail | GET /{id}?email= |
GET /message?id={id}&email= |
GET /{uid}?email=&folder= |
| Mark as read | POST /{id}/read?email= |
POST /message/read?id={id}&email= |
POST /{uid}/read?email=&folder= |
| Mark as unread | POST /{id}/unread?email= |
POST /message/unread?id={id}&email= |
POST /{uid}/unread?email=&folder= |
| Delete | DELETE /{id}?email= |
DELETE /message?id={id}&email= |
DELETE /{uid}?email=&folder= |
All paths are prefixed with
/api/{provider}/messages(Gmail/IMAP) or/api/outlook/(Outlook's special routing).
Extra fields in message detail: body: {content, contentType}, cc, attachments: [{id, filename, mimeType, size}]
4. Send and Reply
Send a new email — POST /api/{p}/send:
{
"email": "sender@example.com",
"toRecipients": [{"name": "recipient", "address": "to@example.com"}],
"ccRecipients": [],
"bccRecipients": [],
"subject": "subject",
"body": "body (HTML supported)",
"contentType": "html",
"attachments": []
}
Reply to an email:
| Provider | Endpoint | Body |
|---|---|---|
| Gmail | POST /gmail/reply |
{email, messageId, body, contentType} |
| Outlook | POST /outlook/message/reply?id={id}&email= |
{body, contentType} |
| IMAP | POST /imap/reply |
{email, uid, folder, body, contentType} |
5. Search (Gmail only)
GET /api/gmail/search?email={email}&q={keyword}
| Parameter | Description |
|---|---|
q |
keyword (searches subject, body, sender) |
from |
sender address |
dateFrom / dateTo |
date range YYYY-MM-DD |
hasAttachment |
true/false |
isUnread |
true/false |
offset / limit |
pagination |
6. Attachment Download
| Provider | Method | Endpoint | Body |
|---|---|---|---|
| Gmail | POST | /gmail/messages/{messageId}/attachment |
{email, attachmentId} |
| Outlook | POST | /outlook/attachment |
{email, messageId, attachmentId} |
| IMAP | POST | /imap/attachment |
{email, uid, folder, partId} |
The response result.data is base64-encoded; decode it and save to a file.
Gmail uses POST because the attachmentId may exceed URL length limits.
7. Draft Management
Gmail:
| Operation | Method | Endpoint |
|---|---|---|
| List drafts | GET | /gmail/drafts?email=&limit= |
| Get draft detail | GET | /gmail/drafts/{draftId}?email= |
| Create draft | POST | /gmail/drafts — body: {email, to, cc, subject, body, contentType} |
| Update draft | PUT | /gmail/drafts/{draftId} — same body as create |
| Delete draft | DELETE | /gmail/drafts/{draftId}?email= |
Outlook:
| Operation | Method | Endpoint |
|---|---|---|
| Create draft | POST | /outlook/drafts — body: {email, toRecipients, subject, body, contentType} |
| Update draft | PUT | /outlook/drafts?id={draftId}&email= — same body as create |
| Delete draft | DELETE | /outlook/drafts?id={draftId}&email= |
| Send draft | POST | /outlook/drafts/send?id={draftId}&email= |
IMAP:
| Operation | Method | Endpoint |
|---|---|---|
| Create draft | POST | /imap/drafts — body: {email, toRecipients, subject, body, contentType} |
| Update draft | PUT | /imap/drafts/{uid} — body: {email, folder, toRecipients, subject, body, contentType} |
| Delete draft | DELETE | /imap/drafts/{uid}?email=&folder= |
| Send draft | POST | /imap/drafts/{uid}/send — body: {email, folder} |
8. Label Management (Unified Interface)
| Operation | Method | Endpoint |
|---|---|---|
| List labels | GET | /labels?provider=&email= |
| Get a single label | GET | /labels/{labelId} |
| Create label | POST | /labels — body: {name, color, provider, email, visible} |
| Update label | PUT | /labels/{labelId} — body: {name, color, order, visible} |
| Delete label | DELETE | /labels/{labelId} |
| Get email's labels | GET | /mails/{p}/{email}/labels?mailId= |
| Add label to email | POST | /mails/{p}/{email}/labels?mailId= — body: {"labelId": "..."} |
| Bulk set labels | PUT | /mails/{p}/{email}/labels?mailId= — body: {"labelIds": [...]} |
| Remove label from email | DELETE | /mails/{p}/{email}/labels?mailId=&labelId= |
| Get emails under a label | GET | /labels/{labelId}/mails?provider=&email=&limit=&offset= |
Gmail native labels:
- List labels:
GET /api/gmail/labels?email= - Modify message labels:
POST /api/gmail/messages/{id}/labels— body:{email, addLabelIds, removeLabelIds} - Sync remote labels:
POST /api/gmail/labels/sync?email=
9. Outlook Categories
Outlook uses Categories instead of Labels.
| Operation | Method | Endpoint |
|---|---|---|
| Get categories | GET | /outlook/categories?email= |
| Sync categories | POST | /outlook/categories/sync?email= |
| Create category | POST | /outlook/categories/create?email= — body: {displayName, color} |
| Update category | PUT | /outlook/categories/update?email=&categoryId= — body: {displayName, color} |
| Delete category | DELETE | /outlook/categories/delete?email=&categoryId= |
| Modify message categories | POST | /outlook/message/categories?id=&email= — body: {addCategories, removeCategories} |
coloruses Outlook preset valuespreset0~preset24.
10. Automation Rules
| Operation | Method | Endpoint |
|---|---|---|
| List all rules | GET | /rules?provider=&email= |
| Get a single rule | GET | /rules/{ruleId} |
| Create rule | POST | /rules |
| Update rule | PUT | /rules/{ruleId} |
| Delete rule | DELETE | /rules/{ruleId} |
| Enable/disable | POST | /rules/{ruleId}/toggle |
| Run rule on a message | POST | /rules/execute — body: {provider, email, mailId} |
| Test rule match | POST | /rules/{ruleId}/test — same body as above |
Create rule body:
{
"name": "rule name",
"description": "description",
"provider": "gmail",
"email": "xxx@gmail.com",
"enabled": true,
"conditions": [
{"field": "from|to|subject|body|has_attachment", "operator": "contains|equals|regex|...", "value": "..."}
],
"conditionLogic": "and",
"actions": [
{"type": "add_label|remove_label|mark_as_read|mark_as_unread|archive|delete|auto_reply|agent_handle", "value": "..."}
],
"priority": 1,
"stopOnMatch": false
}
Action types:
| type | value | Description |
|---|---|---|
add_label |
label ID | add a label |
remove_label |
label ID | remove a label |
mark_as_read |
omit | mark as read |
mark_as_unread |
omit | mark as unread |
archive |
omit | archive |
delete |
omit | delete |
auto_reply |
reply text | auto-reply with fixed content |
agent_handle |
Agent ID | trigger an Agent to handle the email |
Rules are executed automatically when the polling engine detects a new email; no manual call is required.
auto_replyandagent_handlesupport all three mailbox types.
11. Authorization Management
| Operation | Method | Endpoint |
|---|---|---|
| Gmail OAuth | POST | /gmail/auth/initiate?loginHint={email} — opens browser for authorization |
| Gmail status | GET | /gmail/auth/status?email= |
| Outlook OAuth | POST | /outlook/auth/initiate — opens browser for authorization |
| Outlook status | GET | /outlook/auth/status?email= |
12. Folders
| Operation | Method | Endpoint |
|---|---|---|
| IMAP folder list | GET | /imap/folders?email= |
| Outlook folder list | GET | /outlook/folders?email= |
Gmail folders are fixed: inbox, sent, drafts, trash, spam, archive.
Data Sync Mechanism
The email system uses local cache + periodic polling:
- Write operations (send, mark, delete, label): update local and remote simultaneously, no delay
- Read operations (query, search): return the local cache; may be delayed (default 30-second polling)
- Remote changes (the user operates from the official web UI): wait for the next polling cycle to sync
Storage path: ~/.desirecore/mail/{provider}/{email}/ (index.json, messages/, sync.json)
Error Handling
| Status code | Reason | Handling |
|---|---|---|
| 400 | parameter error | check the request parameters |
| 401 | authorization expired | handle per Rule 1; do not try other channels |
| 404 | resource not found | sync first and retry (Rule 3) |
| 500 | internal error | tell the user to retry later |
IMAP note: domestic Chinese mailboxes (QQ, 163) require an "app password" instead of the login password. Use /imap/test to validate the configuration in advance.