feat: skills i18n 改造(schemaVersion 1.1,零向后兼容) (#1)

* 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>
This commit is contained in:
2026-05-05 00:26:33 +08:00
committed by GitHub
parent 1c107a9344
commit 1f7c8b9673
59 changed files with 10533 additions and 2014 deletions

View File

@@ -0,0 +1,349 @@
<!-- locale: zh-CN -->
# mail-operations 技能
## L0一句话摘要
通过本地 REST API 收发邮件、搜索、标签管理和自动规则,支持 Gmail / Outlook / IMAP。
## L1概述与使用场景
### 能力描述
mail-operations 是一个**流程型技能Procedural Skill**,通过 DesireCore 本地 REST API 操作邮件系统。支持 GmailOAuth2、OutlookMSAL和 IMAP/SMTPQQ、163、Yahoo 等)三种邮箱类型,涵盖收发邮件、搜索、标签管理、附件下载、草稿管理和自动规则。
### 使用场景
- 用户需要查看收件箱、发送或回复邮件
- 用户需要搜索特定邮件或管理邮件标签/分类
- 用户需要下载附件或管理草稿
- 用户需要设置自动回复规则或触发智能体处理邮件
### 核心价值
- **统一接口**:三种邮箱通过统一 API 操作,降低使用复杂度
- **本地安全**:所有操作通过本地 API 完成,无需暴露凭证
- **智能联动**:支持自动规则和智能体邮件处理
## L2详细规范
## API 基础信息
- **Base URL**: `https://127.0.0.1:62000/api`
- **认证**: 无需认证(本地服务)
- **Content-Type**: `application/json`
- **SSL**: 使用 `curl -k` 跳过自签名证书验证
- **响应格式**: 成功 `{"code": 1, "msg": "Success", "result": ...}`,失败 `{"code": 0, "msg": "错误信息"}`
---
## 强制行为规则
以下规则优先级最高,每次操作必须遵守。
### 规则 1只能通过本地 API 操作
所有邮件操作**必须且只能**通过 `https://127.0.0.1:62000` 完成。**禁止**调用外部邮件客户端或浏览器。
如果 API 返回 **401授权过期**
1. 告知用户该账户授权已失效
2. 提示在 DesireCore 中重新授权
3. Gmail: `POST /api/gmail/auth/initiate?loginHint={email}`Outlook: `POST /api/outlook/auth/initiate`
### 规则 2操作前先确认账户
执行任何操作前,**必须先调用 `GET /api/accounts`** 获取账户列表:
- 按邮箱地址或域名匹配用户指定的账户("QQ 邮箱"→ imap 中 `qq.com`
- 仅有一个账户且用户说"我的邮箱"→ 直接使用
- 找不到匹配账户 → 告知用户并提示添加
### 规则 3查询为空时自动同步
查询返回空列表或找不到指定数据时:
1. 调用 `POST /{provider}/messages/fetch` 同步远程数据
2. **自动重试**原查询
3. 仍为空才告知用户
### 规则 4写操作后提示刷新
发送、回复、删除、标记、标签变更等写操作成功后,提示:`操作已完成。需要我帮你刷新当前邮箱页面以查看最新状态吗?`
---
## 三种邮箱的差异速查
| 功能 | Gmail | Outlook | IMAP |
|------|-------|---------|------|
| 授权 | OAuth2 | OAuth2 (MSAL) | 密码/授权码 |
| Provider 路径 | `/gmail/` | `/outlook/` | `/imap/` |
| 邮件详情 | 路径参数 `/{id}` | 查询参数 `?id={id}` | UID `/{uid}?folder=` |
| 搜索 | 支持 | 不支持 | 不支持 |
| 草稿 | 支持(含列表/详情) | 支持(创建/发送) | 支持(创建/发送) |
| 附件下载 | 支持 | 支持 | 支持 |
| 标签/分类 | 原生标签 | Categories | 仅本地标签 |
| 自动规则 | 支持 | 支持 | 支持 |
---
## 核心操作
以下 `{p}` 代表 provider`gmail``outlook``imap``{email}` 需 URL 编码。
### 1. 账户管理
| 操作 | 方法 | 端点 |
|------|------|------|
| 获取所有账户 | GET | `/accounts` |
| 获取账户含设置 | GET | `/accounts-with-settings` |
| 删除账户 | DELETE | `/accounts/{p}/{email}` |
| 获取账户设置 | GET | `/accounts/{p}/{email}/settings` |
| 更新账户设置 | PUT | `/accounts/{p}/{email}/settings` |
| 更新显示名称 | PUT | `/accounts/{p}/{email}/displayName` — body: `{"displayName": "..."}` |
**IMAP 专属**
| 操作 | 方法 | 端点 |
|------|------|------|
| 获取预设邮箱配置 | GET | `/imap/presets` — 返回 QQ/163/Yahoo 等服务器配置 |
| 测试 IMAP 连接 | POST | `/imap/test` — body: `{email, password, imap: {host, port, secure}, smtp: {host, port, secure}}` |
| 添加 IMAP 账户 | POST | `/imap/accounts` — body 同上,加 `displayName` |
### 2. 邮件列表与同步
| 操作 | 方法 | 端点 | 参数 |
|------|------|------|------|
| 查询本地缓存 | GET | `/{p}/messages` | `email, offset, limit, folder` |
| 远程同步 | POST | `/{p}/messages/fetch` | `email, limit, folder` |
| 手动触发同步 | POST | `/sync` | `provider, email` |
**folder 取值**Gmail/Outlook: `inbox, sent, drafts, trash, spam, archive`IMAP: `INBOX, Sent, Drafts, Trash` 等。
**响应格式**(邮件列表项):
```json
{
"id": "消息ID", "subject": "主题",
"from": {"name": "...", "address": "..."},
"to": [{"name": "...", "address": "..."}],
"date": "ISO8601", "snippet": "摘要",
"isRead": true, "hasAttachments": false,
"labels": ["INBOX"], "folder": "inbox"
}
```
### 3. 单封邮件操作
| 操作 | Gmail | Outlook | IMAP |
|------|-------|---------|------|
| 获取详情 | GET `/{id}?email=` | GET `/message?id={id}&email=` | GET `/{uid}?email=&folder=` |
| 标记已读 | POST `/{id}/read?email=` | POST `/message/read?id={id}&email=` | POST `/{uid}/read?email=&folder=` |
| 标记未读 | POST `/{id}/unread?email=` | POST `/message/unread?id={id}&email=` | POST `/{uid}/unread?email=&folder=` |
| 删除 | DELETE `/{id}?email=` | DELETE `/message?id={id}&email=` | DELETE `/{uid}?email=&folder=` |
> 所有路径前缀为 `/api/{provider}/messages`Gmail/IMAP或 `/api/outlook/`Outlook 特殊路由)。
**邮件详情额外字段**`body: {content, contentType}`, `cc`, `attachments: [{id, filename, mimeType, size}]`
### 4. 发送与回复
**发送新邮件**`POST /api/{p}/send`
```json
{
"email": "sender@example.com",
"toRecipients": [{"name": "收件人", "address": "to@example.com"}],
"ccRecipients": [],
"bccRecipients": [],
"subject": "主题",
"body": "正文(支持 HTML",
"contentType": "html",
"attachments": []
}
```
**回复邮件**
| Provider | 端点 | 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. 搜索(仅 Gmail
`GET /api/gmail/search?email={email}&q={keyword}`
| 参数 | 说明 |
|------|------|
| `q` | 关键词(搜索主题、正文、发件人) |
| `from` | 发件人地址 |
| `dateFrom` / `dateTo` | 日期范围 YYYY-MM-DD |
| `hasAttachment` | true/false |
| `isUnread` | true/false |
| `offset` / `limit` | 分页 |
### 6. 附件下载
| Provider | 方法 | 端点 | Body |
|----------|------|------|------|
| Gmail | POST | `/gmail/messages/{messageId}/attachment` | `{email, attachmentId}` |
| Outlook | POST | `/outlook/attachment` | `{email, messageId, attachmentId}` |
| IMAP | POST | `/imap/attachment` | `{email, uid, folder, partId}` |
响应 `result.data` 为 base64 编码,解码后保存文件。
> Gmail 使用 POST 因为 attachmentId 可能超出 URL 长度限制。
### 7. 草稿管理
**Gmail**
| 操作 | 方法 | 端点 |
|------|------|------|
| 获取草稿列表 | GET | `/gmail/drafts?email=&limit=` |
| 获取草稿详情 | GET | `/gmail/drafts/{draftId}?email=` |
| 创建草稿 | POST | `/gmail/drafts` — body: `{email, to, cc, subject, body, contentType}` |
| 更新草稿 | PUT | `/gmail/drafts/{draftId}` — body 同创建 |
| 删除草稿 | DELETE | `/gmail/drafts/{draftId}?email=` |
**Outlook**
| 操作 | 方法 | 端点 |
|------|------|------|
| 创建草稿 | POST | `/outlook/drafts` — body: `{email, toRecipients, subject, body, contentType}` |
| 更新草稿 | PUT | `/outlook/drafts?id={draftId}&email=` — body 同创建 |
| 删除草稿 | DELETE | `/outlook/drafts?id={draftId}&email=` |
| 发送草稿 | POST | `/outlook/drafts/send?id={draftId}&email=` |
**IMAP**
| 操作 | 方法 | 端点 |
|------|------|------|
| 创建草稿 | POST | `/imap/drafts` — body: `{email, toRecipients, subject, body, contentType}` |
| 更新草稿 | PUT | `/imap/drafts/{uid}` — body: `{email, folder, toRecipients, subject, body, contentType}` |
| 删除草稿 | DELETE | `/imap/drafts/{uid}?email=&folder=` |
| 发送草稿 | POST | `/imap/drafts/{uid}/send` — body: `{email, folder}` |
### 8. 标签管理(统一接口)
| 操作 | 方法 | 端点 |
|------|------|------|
| 获取标签列表 | GET | `/labels?provider=&email=` |
| 获取单个标签 | GET | `/labels/{labelId}` |
| 创建标签 | POST | `/labels` — body: `{name, color, provider, email, visible}` |
| 更新标签 | PUT | `/labels/{labelId}` — body: `{name, color, order, visible}` |
| 删除标签 | DELETE | `/labels/{labelId}` |
| 获取邮件标签 | GET | `/mails/{p}/{email}/labels?mailId=` |
| 添加邮件标签 | POST | `/mails/{p}/{email}/labels?mailId=` — body: `{"labelId": "..."}` |
| 批量设置标签 | PUT | `/mails/{p}/{email}/labels?mailId=` — body: `{"labelIds": [...]}` |
| 移除邮件标签 | DELETE | `/mails/{p}/{email}/labels?mailId=&labelId=` |
| 获取标签下邮件 | GET | `/labels/{labelId}/mails?provider=&email=&limit=&offset=` |
**Gmail 原生标签**
- 获取标签列表:`GET /api/gmail/labels?email=`
- 修改邮件标签:`POST /api/gmail/messages/{id}/labels` — body: `{email, addLabelIds, removeLabelIds}`
- 同步远程标签:`POST /api/gmail/labels/sync?email=`
### 9. Outlook 分类
Outlook 使用 Categories 而非 Labels。
| 操作 | 方法 | 端点 |
|------|------|------|
| 获取分类 | GET | `/outlook/categories?email=` |
| 同步分类 | POST | `/outlook/categories/sync?email=` |
| 创建分类 | POST | `/outlook/categories/create?email=` — body: `{displayName, color}` |
| 更新分类 | PUT | `/outlook/categories/update?email=&categoryId=` — body: `{displayName, color}` |
| 删除分类 | DELETE | `/outlook/categories/delete?email=&categoryId=` |
| 修改邮件分类 | POST | `/outlook/message/categories?id=&email=` — body: `{addCategories, removeCategories}` |
> `color` 使用 Outlook 预设值 `preset0` ~ `preset24`。
### 10. 自动规则
| 操作 | 方法 | 端点 |
|------|------|------|
| 获取所有规则 | GET | `/rules?provider=&email=` |
| 获取单个规则 | GET | `/rules/{ruleId}` |
| 创建规则 | POST | `/rules` |
| 更新规则 | PUT | `/rules/{ruleId}` |
| 删除规则 | DELETE | `/rules/{ruleId}` |
| 启用/禁用 | POST | `/rules/{ruleId}/toggle` |
| 对邮件执行规则 | POST | `/rules/execute` — body: `{provider, email, mailId}` |
| 测试规则匹配 | POST | `/rules/{ruleId}/test` — body 同上 |
**创建规则 body**
```json
{
"name": "规则名",
"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
}
```
**动作类型说明**
| type | value | 说明 |
|------|-------|------|
| `add_label` | 标签 ID | 添加标签 |
| `remove_label` | 标签 ID | 移除标签 |
| `mark_as_read` | 省略 | 标记已读 |
| `mark_as_unread` | 省略 | 标记未读 |
| `archive` | 省略 | 归档 |
| `delete` | 省略 | 删除 |
| `auto_reply` | 回复文本 | 自动回复固定内容 |
| `agent_handle` | Agent ID | 触发智能体处理邮件 |
> 规则在轮询引擎检测到新邮件时**自动执行**,无需手动调用。`auto_reply` 和 `agent_handle` 支持全部三种邮箱类型。
### 11. 授权管理
| 操作 | 方法 | 端点 |
|------|------|------|
| Gmail OAuth | POST | `/gmail/auth/initiate?loginHint={email}` — 打开浏览器授权 |
| Gmail 状态 | GET | `/gmail/auth/status?email=` |
| Outlook OAuth | POST | `/outlook/auth/initiate` — 打开浏览器授权 |
| Outlook 状态 | GET | `/outlook/auth/status?email=` |
### 12. 文件夹
| 操作 | 方法 | 端点 |
|------|------|------|
| IMAP 文件夹列表 | GET | `/imap/folders?email=` |
| Outlook 文件夹列表 | GET | `/outlook/folders?email=` |
> Gmail 文件夹固定inbox, sent, drafts, trash, spam, archive。
---
## 数据同步机制
邮件系统采用**本地缓存 + 定期轮询**
- **写操作**(发送、标记、删除、标签):同时更新本地和远程,无延迟
- **读操作**(查询、搜索):返回本地缓存,可能有延迟(默认 30 秒轮询)
- **远程变更**(用户在官方页面操作):需等待下次轮询同步
**存储路径**`~/.desirecore/mail/{provider}/{email}/`index.json, messages/, sync.json
---
## 错误处理
| 状态码 | 原因 | 处理 |
|--------|------|------|
| 400 | 参数错误 | 检查请求参数 |
| 401 | 授权过期 | **按规则 1 处理**,不要尝试其他途径 |
| 404 | 资源不存在 | 先同步再重试(规则 3 |
| 500 | 内部错误 | 告知用户稍后重试 |
**IMAP 注意**国内邮箱QQ、163需使用"授权码"而非登录密码。用 `/imap/test` 预先验证配置。