first commit

This commit is contained in:
2026-09-02 11:44:52 +08:00
commit 0c8fa2653e
309 changed files with 57278 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
---
name: dingtalk-shared
description: 钉钉(DingTalk) MultiSkill 的轻量共享入口。Use when 用户泛称 DWS/钉钉操作但未明确产品、请求跨产品编排、需要 URL 类型预检或产品边界消歧。清晰的单产品操作优先使用对应 dingtalk-* 子 skill;本 skill 只提供全局执行契约和按需 reference 导航,不承载产品命令全集。
metadata:
cli_version: ">=0.2.14"
category: shared
requires:
bins:
- dws
---
# DWS 共享执行契约
本文件只在泛称 DWS、跨产品流程、URL 预检或意图不清时作为入口。明确单产品请求直接使用对应 `dingtalk-*` skill;已经内嵌最小执行契约的产品根 Skill 不需要先完整读取本文件。
<!-- DWS_RUNTIME_CONTRACT_START -->
## 最小 DWS 执行契约
- 只通过 `dws` CLI 操作钉钉;结构化读取使用 `--format json`,按真实返回判断结果。
- 已知命令直接执行。只有 leaf 参数或安全语义不确定时读取精确 Schema,只有 Cobra flag 不确定时读取精确 leaf Help;不要加载产品级 Catalog 代替选路。
- 不猜命令、flag、字段、ID、账号或时间。后续 ID 必须来自真实返回;零命中、多候选或类型不明时停止并消歧。
- 解析目标、读取上下文和最终执行必须使用同一 profile;不得跨组织复用 userId、openDingTalkId 或 openConversationId。多账号组织只使用明确的 `isOrgCurrent=true` 默认账号;没有默认账号时要求用户指定,禁止选择第一项、最近登录或最近使用账号。
- 不输出或记录 token、refresh token、appSecret、webhook token 等凭据;宿主已注入认证时不要索要凭据。
- 写操作必须符合用户明确意图。是否需要确认以最终 Runtime gate 和 Schema 为准;需要确认时先说明对象、动作与影响,再追加 `--yes`
- 写后按任务结果契约验证;不能仅凭退出码宣称成功。部分结果、未知投递状态和失败项必须如实保留。
- 时间戳面向用户展示时转换为带时区的可读时间;默认使用当前会话时区,必要时同时保留原值。
- 遇到认证、权限、profile、confirmation 或未知错误时,只加载 `dingtalk-shared` 中对应 reference;不要连续猜测替代命令。
<!-- DWS_RUNTIME_CONTRACT_END -->
产品或跨产品规则在最小契约之上增量加载。用户已明确产品内容意图时,意图优先于 URL 形态;多账号选择与跨组织规则读取 [`../dingtalk-misc/references/profile.md`](../dingtalk-misc/references/profile.md)。本地文件、产品边界和跨产品传递规则只在对应任务中加载,避免把全局手册放入每个单产品请求。
## 渐进加载
只读取当前任务需要的文件,不要一次性加载全部 shared references
| 当前情况 | 必读内容 |
|---|---|
| 已明确单一产品 | 对应 `../dingtalk-*/SKILL.md`;不读路由 reference |
| 泛称 DWS、需要选择产品 | [routing.md](references/routing.md) |
| 跨产品、多步骤、汇总或报告 | [workflow-routing.md](references/workflow-routing.md) |
| 输入含 alidocs、shanji 等钉钉 URL 且类型不明 | [url-patterns.md](references/url-patterns.md) |
| 产品边界仍然难以判断 | [intent-guide.md](references/intent-guide.md) 的相关章节 |
| 认证、全局 flag 或输出格式问题 | [global-reference.md](references/global-reference.md) |
| 命令已经返回错误 | [error-codes.md](references/error-codes.md);只查错误对应章节 |
| `confirmation_required` / 写操作确认 | [confirmation.md](references/confirmation.md) |
| 命令发现、Schema / `--compact` / `--all` | [schema-usage.md](references/schema-usage.md) |
| 怀疑能力不支持 | [capability-limits.md](references/capability-limits.md) |
| 批量/多源采集 | [conventions.md](references/recipes/conventions.md) |
| 固定短流程 | [lite-catalog.md](references/recipes/lite-catalog.md) 对应章节 |
产品命令、脚本和字段细节位于对应产品 skill,不在 `dingtalk-shared` 重复维护。
## 本 skill 作为入口时的路由顺序
1. 先识别明确的产品内容意图;明确意图直接进入对应产品。仅当输入包含钉钉 URL
且类型不明确或意图与链接类型可能冲突时,读取 `url-patterns.md` 识别节点类型。
2. 请求包含多个时序步骤、跨产品数据传递或汇总报告:即使 URL 已识别,也要读取
`workflow-routing.md`,按行动指南组合需要的产品 skill;当前发布包不包含独立
scenario skill。
3. 请求是单产品操作但产品不明确:读取 `routing.md`,再显式读取目标产品
`SKILL.md`
4. `doc/drive/wiki``aitable/sheet``calendar/minutes` 等边界仍不清楚:
只读取 `intent-guide.md` 的对应章节。
5. 仍无法判断时向用户追问,不要猜测产品或命令。
## 跨 skill 执行
- 正文中的相对 `Read` 链接是运行时依赖;`metadata.requires.skills` 不会自动加载。
- 选择目标产品后,以目标 skill 的命令、参数和风险规则为准。
- 多步骤流程按顺序传递真实返回值;可以并行的只读采集按对应 workflow/reference
执行,写操作默认串行并逐步验证。
- 产品 skill 已内联的清晰操作直接执行;仅在遇到该 skill 未覆盖的参数或边界时读取
更深层 reference。
## 错误最短路径
1. `unknown command` / `unknown flag`:运行对应层级 `--help`,按公开 flag 修正后最多重试一次;命令选择不确定时读 [schema-usage.md](references/schema-usage.md)。
2. `reason=confirmation_required`:按 [confirmation.md](references/confirmation.md) 处理,不要当普通校验错误放弃或静默加 `--yes`
3. 认证或权限错误:读取 `global-reference.md``error-codes.md` 对应章节。
4. 其他错误:优先读取 JSON 错误中的 `retryable``retry_after_seconds`
`next_retry_at``hint``actions`。只有明确 `retryable=true` 时才按服务端节奏重试;
缺少重试语义时用 `--verbose` 获取诊断并停止,不连续尝试替代命令。
5. 明确不支持的能力:说明边界,不通过其他接口绕过。
@@ -0,0 +1,31 @@
# 已知能力限制
遇到以下操作时,**不要重试或变通**,直接告知用户当前不支持并建议在钉钉客户端操作。
## chat
| 不支持的操作 | 说明 |
|------------|------|
| 撤回个人身份发送的消息 | 只有 `send-by-bot` 发送的消息才能通过 `recall-by-bot` 撤回。个人身份 (`chat message send`) 发送的消息无法通过 API 撤回 |
## doc
| 不支持的操作 | 说明 |
|------------|------|
| 文档权限管理 | 不支持通过 CLI 设置文档分享/权限 |
## aitable
| 不支持的操作 | 说明 |
|------------|------|
| 创建公式/查找引用等高级字段类型 | 部分高级字段类型暂不支持 API 创建 |
## minutes
| 不支持的操作 | 说明 |
|------------|------|
| 跨听记的全局热词修正 | `replace-text` 仅修正**当前这一篇听记**的文字,不会影响后续新听记的语音识别结果。要让某个词长期不再被识别错,必须额外引导用户使用 `hot-word add` 添加个人热词 |
---
> 此文件随产品能力迭代更新。新增限制时按产品分类追加即可。
@@ -0,0 +1,26 @@
# 受控渠道与阿里巴巴组织登录
## 使用场景
在以下任一场景读取本参考:
- 目标组织是阿里巴巴;
- 登录返回 `CHANNEL_REQUIRED``channel_not_allowed``enterprise_not_authorized` 或“应用暂不受信任”;
- 用户提到渠道码、`DWS_CHANNEL`、渠道白名单或渠道归因;
- 需要判断 `DWS_CHANNEL``DINGTALK_DWS_AGENTCODE` 的边界。
## 核心契约
-`DWS_CHANNEL` 作为产品/分发渠道 `channelCode`。CLI 在登录权限检查和后续 MCP 请求中把它发送为 `x-dws-channel`
-`DINGTALK_DWS_AGENTCODE` 作为执行 Agent 身份。两者是独立维度,禁止互相回填或复用。
- 在受控渠道组织中,把 `DWS_CHANNEL` 同时加到 `auth login` 和每一条后续 `dws` 命令。只在单条命令作用域设置,禁止写入 shell profile 或对其他组织全局导出。
- 仅使用与真实宿主/业务场景匹配的已登记渠道。禁止为了通过登录随机尝试其他渠道或伪装成别的产品。
- 把静态 `channelCode` 视为公开路由标识,不视为密钥或可信归因凭证。长期方案必须由服务端校验宿主身份并签发短期、绑定组织和渠道的会话凭证。
## 排查顺序
1. 运行 `dws profile list --format json`,解析目标组织的稳定 `profile`
2. 确认当前宿主配置的渠道与真实业务场景匹配。
3. 使用命令级 `DWS_CHANNEL` 重新执行 `dws auth login --profile ... --format json`
4. 使用相同 `DWS_CHANNEL``profile` 执行一个最小只读产品命令验证。
5. 若仍失败,加 `--verbose` 重试一次并按原始服务端错误分类;禁止轮询尝试其他渠道。
@@ -0,0 +1,59 @@
# 确认门禁协议
写操作与高风险操作必须以最终 Runtime gate / leaf Schema 的 `confirmation` 为准。
本文件是 multi 布局下的**全局协议**;各产品危险表示例索引见文末,细节仍以产品
reference 为准。
## 确认流程
```text
Step 1 → 展示操作摘要(操作类型 + 目标对象 + 影响范围)
Step 2 → 用户明确回复确认(如「确认」/「好的」)
Step 3 → 在原始命令末尾追加 --yes 执行(不改动业务参数)
```
## `confirmation_required` 识别与重试
非交互环境(Agent/CI,stdin 非 TTY)下,写命令不带 `--yes` 时 CLI **不打印交互提示语**
直接失败并输出结构化错误。识别方式:
- `--format json` 输出(或 stderr)中 `error.reason == "confirmation_required"`
- 错误信息通常含「当前环境无法交互确认」
遇到 `confirmation_required` 时:
1. **不要当普通错误放弃**:把命令、风险等级与关键参数展示给用户,明确这是写/高风险操作
2. 用户显式同意 → 在**原始命令**末尾追加 `--yes` 重试(不改动任何业务参数)
3. 用户拒绝 → 终止,不得改写参数绕过门禁
4. 想先让用户 review 具体请求:加 `--dry-run` 重试——它**不触发确认门禁**,会输出完整调用预览(`invocation.params`),用户确认预览后再换 `--yes` 执行
**禁止**
- 看到 `confirmation_required` 就未经用户同意自动追加 `--yes` 静默重试
-`confirmation_required` 当网络/权限错误处理或重试
-`echo yes | dws ...` 等管道方式喂答案代替 `--yes`(技术上可能被接受,但违背显式知悉意图)
- 换成确认更弱的底层命令来「绕过」门禁
## 与 Schema 的关系
- leaf Schema / Shortcut 中 `confirmation=user_required` → 必须先向用户确认再加 `--yes`
- 不要根据 `effect``risk` 的值自行改写最终 confirmation winner
- 字段解读见 [schema-usage.md](./schema-usage.md)
`error-codes.md` 中对 `reason=confirmation_required` 的一行提示指向本协议;完整步骤以本文为准。
## 高影响操作索引(入口)
完整命令与边界在对应产品 skill;此处只给冷启动索引:
| 产品面 | multi skill | 危险/确认细节落点 |
|---|---|---|
| AI 表格删除类 | `dingtalk-aitable` | 产品 `SKILL.md` / aitable references |
| 日历删除/取消 | `dingtalk-calendar` | calendar references |
| 群成员移除 / 机器人撤回 | `dingtalk-chat` | chat references |
| 文档删除 / 块删除 / 权限降权 | `dingtalk-doc` | doc references |
| 待办删除 | `dingtalk-todo` | todo references |
| 听记全文替换 | `dingtalk-minutes` | minutes references |
| DING 撤回 / OA 拒绝撤销等 | `dingtalk-misc` | `ding` / `oa` 等 references |
不确定时:先 `dws schema --cli-path "<path>" --compact --format json``confirmation`,再按本协议执行。
@@ -0,0 +1,155 @@
# 错误与恢复说明
只查当前错误对应章节。先读取结构化字段,再决定修正、重试、请求用户介入或停止。
## 错误返回格式
```json
{
"error": {
"code": 3,
"category": "validation",
"message": "missing required flag(s): --base-id",
"reason": "...",
"retryable": false,
"hint": "...",
"actions": ["..."]
}
}
```
错误写到 stderr。`code` 是 CLI 退出码,`category` 是稳定大类:`api``auth`
`validation``discovery``internal`。其他字段按错误类型出现,不保证每次都有。
## 错误分类与 Agent 行为
### 可自行修复
- `category=validation``available_flags`/`hint` 给出明确修正:核对 leaf Help,修正
一次;不要猜 flag。
- 自然目标返回 `details.candidates`:零命中或多候选都停止并消歧,禁止选择第一项。
- `reason=confirmation_required`:完整协议见 [confirmation.md](./confirmation.md)
(展示摘要 → 用户显式同意 → 原始命令追加 `--yes`;禁止静默重试、管道喂答案、换成更弱命令)。
### 需用户介入
- 权限不足、资源不存在、配额/权益不足:报告 `server_error_code``trace_id``hint`
`action_url`(如有),不自行改身份或尝试替代接口。
- profile 不存在或同组织多账号无默认:让用户指定 `corpId:userId`,不要选最近账号。
- 未知投递状态:停止重发,先查询状态;没有状态查询能力时如实报告未知。
## 重试规则
- `retryable=true`:遵守 `retry_after_seconds``next_retry_at`;运行时可能已经完成
HTTP 重试,调用方只做一次有界重试并保留相同幂等键。
- `retryable=false`:不要重试。
- 未出现 `retryable`:不要从 `category`、HTTP 文案或“看起来像临时错误”推断可重试;
使用 `--verbose` 收集诊断后停止。
- 超时不会因为盲目增大 `--timeout` 自动变安全。只有任务确实允许更长等待且操作状态可判定时,
才显式调整超时。
## 认证与权限
- 精确的 access-token 拒绝信号会由 Runtime 自动刷新并最多重放一次;不要再包一层“重试两次”。
- `reason=auth_refresh_failed``auth status` 返回 `authenticated=false`:保留原错误,运行
`dws auth status --profile <same-profile> --format json`;按返回 `hint` 恢复,必要时请用户登录。
- HTTP/RPC 403 与普通权限不足不会触发 token 刷新;不要通过切 profile、bot 或 webhook
身份绕过。
- AppKey/AppSecret 缺失只用于应用凭据配置问题;不要向普通业务用户索要凭据。
---
## aitable 高频错误
> 参数体系: `baseId / tableId / fieldId / recordId`。CLI flag 用 kebab-case`--base-id`),JSON 内用 camelCase`baseId`)。
- 参数缺失 / 无效请求 — 还在用旧参数 `dentryUuid` / `--doc` / `--sheet` → 改用 `--base-id` / `--table-id` / `--field-id` / `--record-ids`
- 参数传了但服务端没收到 — flag 用了 camelCase(如 `--baseId`)→ flag 用 kebab-case: `--base-id <ID>`
- `record query --filters` 无结果 — 单选/多选过滤用了 option name 而非 id → 先 `field get` 读取 options,用 option id 过滤
- record create/update 失败 — `cells` key 用了字段名(应为 fieldId);特殊字段格式错误 → 先 `field get` 拿字段目录;url 传 `{"text":"..","link":".."}`
- 更新选项后历史数据异常 — 更新 options 没传完整列表 / 没保留原 id → 先 `field get` 取完整配置,保留已有 option 的 id
- `cannot delete the last table` — 该表是 Base 最后一张表 → 先新建表再删旧表,或用 `base delete`
- `formula` 类型 `not supported yet` — 部分字段类型暂不支持 API 创建 → 复杂字段拆开单独创建,先建基础结构
**排查链路**: `base list``base get`(→tableId) → `field get`(→fieldId) → `record query`(→recordId)。别跳步,别猜 ID。
**批量上限**: record 100 条 / field 15 个 / table·field 详情 10 个。
---
## doc 高频错误
- 文档不存在 / nodeId 无效 — nodeId 或 URL 不正确、文档已删除 → `drive search` / `wiki node search``wiki node list` 重新获取正确 nodeId
- 无下载权限 — 文档分享设置不允许 → 报告用户,建议联系文档所有者
- `update --mode overwrite` 意外清空 — overwrite 会清空原内容后重写 → 默认用 `--mode append`overwrite 前必须跟用户确认
- 块编辑 blockId 无效 — blockId 过期或文档结构已变 → 先 `block list` 刷新获取最新 blockId
- `doc_write_commit_unknown` — 某个分片写入超时,服务端是否已提交无法判断 → 不会自动重试(重放会重复追加)。`details` 给出 `chunksWritten` / `chunksTotal` / `failedStage`;先用 `doc read --node <ID>` 回读确认实际写到哪一片,只有确认未提交才重新执行,再从断点处用 `doc update --mode append` 继续追加
---
## calendar 高频错误
- **误用顶层 `dws calendar` / 臆造 `calendar list`** — 只输入 `dws calendar` 或尝试不存在的 `calendar list` 会打印大段 Usage,易导致上下文暴涨与响应变慢 → **改用** `dws calendar event list --start "<ISO>" --end "<ISO>" --format json`,或加载 `dingtalk-calendar` 后按其日程查询 recipe 执行;详见 `dingtalk-calendar`「CLI 命令树与黄金路径」「反模式(禁止)」
- 时间格式错误 — 未使用 ISO-8601 格式 → 标准格式: `2026-03-10T14:00:00+08:00`
- 会议室搜索报错 / 返空 — 企业会议室超 100 条未分组查询 → 先 `room list-groups` → 按 `--group-id` 逐组搜索
- 参与者 / 会议室添加失败 — eventId 不正确 → 先 `event list``event create` 获取正确 eventId
- `roomId invalid` / 订房失败 — 把会议室**展示名**或用户口语当成了 `roomId` → **只能**使用 `room search` 返回 JSON 中的 `rooms[].roomId` 填入 `room add --rooms`;不得以中文名、楼层编号文案充当 ID
- `unknown flag: --query`(会议室)— `room search` **不支持**按名称搜索 → 先 `room list-groups` 再按分组 `room search`,在返回列表中匹配名称后取 `roomId`(见 calendar.md
---
## chat 高频错误
- 群聊读取优先 `chat +chat-messages --group <群名或ID>`;单聊先解析唯一用户 ID 再读取。
自然群名多候选时读取 `details.candidates` 并让用户消歧,不取第一项。
- 普通发送优先 `chat +dm``chat +send-to-group``chat +messages-send`;发送类 Shortcut
的最终 Schema 要求确认时,必须先确认再加 `--yes`
- `--group``--user``--open-dingtalk-id` 等目标参数互斥:按 leaf Help 只传一类目标。
- `+messages-send``--text`/`--markdown`/`--media-id`/`--file` 互斥;身份、目标、
凭据和幂等参数还受 user/bot/webhook 能力矩阵约束。
- `ok=false``partial=true`、非空 `failures` 或分页仍有 continuation 都不是完整成功;
必须保留 ledger,不能只返回已成功部分。
- 机器人不在群或当前用户无管理权限:报告真实失败,需要群管理员处理;不改用当前用户身份发送。
- 原子 `chat message send` 只用于 Shortcut 未覆盖的底层消息类型/原始字段。其正文可用
`--text`,不要把某个历史位置参数写法当成所有发送入口的规则。
---
## oa 高频错误
- approve/reject 缺少 taskId — 未先获取审批任务 → 先 `approval tasks --instance-id <ID>` 获取 taskId
- list-initiated 缺少 processCode — 未查询审批表单 → 先 `approval list-forms``detail` 获取 processCode
- 撤销审批失败 — 非本人发起的审批 → `revoke` 只能撤销自己发起的审批
---
## report 高频错误
> 参数体系:`templateId / reportId`。CLI flag 用 kebab-case`--template-id` / `--contents-file`)。`contents` 数组每项含 `key/sort/content/contentType/type` 五个字段,`key/sort/type` 必须严格对齐 `template get` 返回的 `field_name/field_sort/field_type`。
- `INPUT_INVALID_JSON``--contents``--contents-file` 内容非合法 JSON → 检查数组结构 `[{key, sort, content, contentType, type}]`
- `INPUT_FILE_NOT_FOUND``--contents-file` 路径错 / sandbox OS 风格不匹配 → 先确认 sandbox OSWindows: `C:\...` / macOS|Linux: `/...`)后改写
- `INPUT_MISSING_PARAM``--template-id` 或 contents 必填缺失 → 先跑 `dws report template list --format json` 取 templateId
- `MCP_TOOL_ERROR` + `server_error_code: PARAM_ERROR` — 服务端业务校验失败(templateId 错 / `key` 不在模版定义 / 类型不匹配 / 必填空 等多种形态都返回这一个码,且服务端不区分子原因)→ 不要靠错误信息排查具体字段,按提交链路重新走 `template list → template get → entry submit`;连续 ≥ 2 次失败必须停止重试,降级 final_reply
- `MCP_TOOL_ERROR` + 其他 server_error_code — 查看 `technical_detail`;如出现不可读错误(仅含 `root.success当前值`),降级 final_reply 引导用户手动操作
**排查链路**`template list``template get`(取 `result.report_template_fields[]`,每项含 `field_name/field_sort/field_type`)→ 拼 `--contents``field_name → key``field_sort → sort``field_type → type`,再填 `content``contentType``entry submit`。**别跳步、别猜字段名、别自己改写 key 名。**
---
## contact / drive / mail 高频错误
- contact: `dept list-children` 报错 — `--id` 传了非整数值 → deptId 必须为整数,从 `dept search` 获取
- drive: 文件不存在 — dentryUuid 不正确 → `drive list` 逐级浏览获取正确 ID
- drive: 上传失败 / uploadId 无效 — 跳过了 `upload-info` 步骤 → 必须先 `upload-info` 获取上传凭证,再 `commit`
- drive: 文件名报错 — `--file-name` 缺少扩展名 → 必须包含扩展名: `report.pdf`
- mail: 发件地址不正确 — 未先查询可用邮箱 → 先 `mailbox list` 获取邮箱地址
- mail: KQL 搜索无结果 — 查询语法错误 → 字段值含空格用双引号: `subject:\"周报\"`
---
## 通用排查三步法
1. **确认 ID** — 从最顶层资源逐级获取,不猜 ID、不跳步
2. **确认参数** — flag 用 kebab-caseJSON 用 camelCase;特殊字段查产品参考文档确认格式
3. **确认限制** — 检查批量上限和已知约束(各产品注意事项见对应产品参考文档)
@@ -0,0 +1,122 @@
# 全局运行时参考
只在认证、profile、公开全局 flag 或输出格式不确定时读取本文件。命令与参数事实以当前
分支的 leaf Schema 和 Cobra Help 为准。
## 认证与 profile
日常业务命令会使用本地 OAuth 登录态,并在明确的 access-token 拒绝信号下自动刷新、
最多重放当前调用一次。不要从错误文本猜测 token 状态,也不要自行循环重试。
```bash
# 查看当前 profile 的登录态;读取 authenticated/token_valid 等真实字段
dws auth status --format json
# 列出全部账号及 isCurrent/isOrgCurrent
dws profile list --format json
# 本机浏览器登录;无头环境使用 --device
dws auth login --recommend --format json
dws auth login --device --recommend --format json
# 只指定本次组织/账号,不持久切换默认 profile
dws auth status --profile <corpId>:<userId> --format json
```
- 同一条 IM 链路的目标解析、读取与写入必须使用同一 `--profile`
- `profile list` 本身不刷新 token`auth status --profile ...` 只检查/刷新选中的 token
slot,不修改 `currentProfile`
- `auth logout` 默认退出全部账号;传 `--profile` 才缩小范围。`auth reset` 会清除本地
token,只有确认登录态需要重建时才使用。
- 宿主已注入认证时不要向用户索要 token、refresh token、AppSecret 或 webhook token。
### 可迁移认证包
只有用户明确要求迁移登录态时才使用:
```bash
dws auth export --output dws-auth.tar.gz
dws auth import --input dws-auth.tar.gz
dws auth status --format json
```
认证包包含敏感凭据和解密材料;不得打印其内容,迁移完成后按用户的安全策略删除。
平台/密钥后端限制以 `dws auth export --help` 与命令返回为准,不自行绕过。
## 公开全局 flag
| flag | 语义 | 默认/约束 |
|---|---|---|
| `--format`, `-f` | `json\|table\|raw\|pretty\|ndjson\|csv` | `json` |
| `--fields` | 逗号分隔的输出字段筛选 | 空 |
| `--jq` | jq 表达式过滤输出 | 空 |
| `--profile` | 一次性指定组织或账号;支持 CSV 多选的命令由运行时决定 | 当前 profile |
| `--timeout` | HTTP 请求超时秒数 | `30` |
| `--dry-run` | 请求预览 | 只有 leaf 明确支持时才代表可执行预览 |
| `--yes`, `-y` | 通过 Runtime confirmation gate | 仅在用户确认且最终 Schema 要求时追加 |
| `--verbose`, `-v` | 增加诊断信息 | `false` |
| `--debug` | 输出内部调试诊断 | `false`;不得泄露凭据 |
| `--mock` | 开发调试 Mock | `false`;不得当作业务成功 |
| `--client-id` / `--client-secret` | OAuth 应用凭据覆盖 | 必须成对、仅在明确配置任务使用 |
公开 Help 不提供通用业务 `--token` flag。Webhook token 等凭据只传给声明该 leaf
参数的命令;不要把某个 leaf 的 `--token` 推广成全局认证方式。
## 输出契约
### 成功输出
`--format json` 直接序列化命令的真实 payload;不存在适用于所有产品的固定
`{"success":true,"body":...}` 包装。读取 leaf 声明的字段,并保留以下完整性信号:
- 分页:`hasMore``nextCursor``nextPageToken` 等当前响应字段;
- 批量/编排:`ok``partial``failures``results` 等当前 Shortcut ledger
- 写入:返回的稳定资源 ID、投递状态或逐项失败,不用退出码代替结果验证。
`table`/`pretty` 面向人工阅读,`raw` 保留原始文本,`ndjson`/`csv` 面向列表流转。
Agent 默认使用 JSON;只有确定列表形状与下游需求时才切换格式。
### 错误输出
JSON 模式下,失败写到 stderr,稳定外层结构为:
```json
{
"error": {
"code": 3,
"category": "validation",
"message": "...",
"reason": "...",
"retryable": false,
"hint": "...",
"actions": ["..."]
}
}
```
`code/category/message` 外的字段按错误类型出现。优先消费 `reason``retryable`
`retry_after_seconds``next_retry_at``details``available_flags``trace_id`
`server_error_code``hint``actions`;不要依赖自由文本子串做自动修复。
## 相关环境变量
| 变量 | 用途 |
|---|---|
| `DWS_CONFIG_DIR` | 覆盖默认 `~/.dws` 配置目录 |
| `DWS_CLIENT_ID` + `DWS_CLIENT_SECRET` | 成对覆盖 OAuth 应用凭据;只设置一个时不会作为完整凭据对使用 |
| `DWS_CHANNEL` | 受控分发渠道;仅在明确渠道任务中按 [channel-login.md](channel-login.md) 使用 |
不要把内部调试/兼容环境变量写进普通业务流程,也不要把凭据持久写入 shell profile。
## 命令自省
```bash
# 决定 Agent 选路、参数映射与确认语义
dws schema --cli-path "chat +messages-send" --compact --format json
# 核对当前二进制真实接受的 flag
dws chat +messages-send --help
```
已知 leaf 直接执行。只有 selection、安全或参数映射不确定时查精确 Schema;只有 flag
拼写不确定时查精确 Help。不要为一个 leaf 加载产品级全量 Catalog。
@@ -0,0 +1,536 @@
# 意图路由指南
当用户请求难以判断归属哪个产品时,参考本指南。
## 易混淆场景快速对照表
| 用户说... | 真实意图 | 应该用 | 不要用 | 理由 |
|-----------|----------|--------|--------|------|
| "搜一下 OAuth2 接入文档" | 搜索开发文档 | `devdoc` | `doc search` | 搜索开放平台技术文档,不是钉钉内部内容 |
| "帮我建一个项目跟踪表" | 创建数据表格 | `aitable` | `doc` / `sheet` | 涉及结构化数据/行列操作,不是富文本文档或电子表格 |
| "帮我写个项目周报" | 创建钉钉文档 | `doc` | `aitable` | 富文本内容创作,不是数据表 |
| "参照这个生成同样的 / 按模板生成 / 复刻 X / 同样的模板 X 月份的" + 已有 alidocs URL | 模板保形生成同形态变体 | `drive copy + drive rename + doc block update` → 加载 `dingtalk-doc` 后执行 `dingtalk-doc/references/04-document.md``template-based-generation` | `doc read + doc create`(重写链) | adoc → markdown 是有损投影,read+create 会丢行高/单元格背景色/字号;copy 在 adoc 层保形复制后只在副本上局部修改 |
| "创建一个电子表格" | 创建表格文档 | `sheet` | `aitable` | Excel 式表格/单元格操作,不是多维表记录 |
| "帮我读一下表格 A1:D10 的数据" | 读取单元格数据 | `sheet` | `aitable` | 按单元格区域读写,不是按记录查询 |
| "这个 alidocs 表格链接帮我看下"(粘贴原始 URL) | 先 probe 节点类型 | `dws drive info --node` → 按 `extension` 路由 | 直接调 `sheet` | `alidocs/i/nodes/{id}` 可能是文档/axls/able/xlsx 等,禁止凭 URL 猜类型 |
| "读一下这个 xlsx 的数据" / xlsx 节点链接 | 下载本地表格文件 | `dws drive download --node` | `sheet range read` | xlsx / xls / xlsm / csv 是上传的本地文件(`contentType=DOCUMENT`),sheet 命令只支持在线表格,必须下载后本地解析 |
| "把这个在线表格导出为 xlsx 文件" | 在线表格格式转换 | `dws sheet export` | `dws drive download` | `export` 是 axls → xlsx 的导出转换;`download` 只能下载已有的 xlsx 节点 |
| "帮我记一下明天要做的事" | 创建个人待办 | `todo` | `doc` | 个人待办提醒,非文档内容 |
| "给自己留一个明天下午的时间块/建个个人日程" | 创建个人日程 | `calendar event create` | `todo` | 个人 schedule 仍属于日历事件,不是待办 |
| "帮我把这个文件传到网盘" | 钉盘上传 | `drive upload` | — | 文件上传是存储层操作,归 drive |
| "上传文件到钉盘/我的文件" | 钉盘上传 | `drive upload` | — | 提到"钉盘/网盘/我的文件"→ drive |
| "上传文件"(未指定目标) | 默认钉盘 | `drive upload` | — | 未明确目标时默认上传到钉盘 |
| "用本地文件覆盖钉盘/知识库里的文件" | 按目标节点类型覆盖已有文件 | 先 `dws drive info --node <目标> --format json` 并记录原 `name``extension=md` 时先用 `dws markdown overwrite --node <目标> --file <本地.md> --dry-run --format json` 预览,确认后改用 `--yes` 执行;其他普通文件用 `dws drive upload --node <目标> --file <本地文件> --file-name "<原name>" --format json``adoc` / `axls` / `able` 切对应内容 skill/reference 再决定写入方式 | 未探测类型就固定走 `drive upload --node`,或普通文件覆盖时省略 `--file-name` | 原生 `.md` 覆盖必须保留 Markdown 专用 diff 预览与确认流程;普通文件省略 `--file-name` 会被隐式重命名;在线文档、在线表格和 AI 表格也不能按普通文件覆盖 |
| "导入文件到我的文档" / "导入到个人文档" | 文件导入为在线文档 | `dws wiki space list --type myWikiSpace``dws doc import --workspace <workspaceId>` | `dws doc import`(不传目标参数) | **doc import 必须传 --folder 或 --workspace 至少一个**,不传会报错;导入到"我的文档"需先获取 workspaceId |
| "把文件导入成在线文档" / "导入 Word/Excel" | 文件格式转换+创建在线文档 | `dws doc import --file <文件> --workspace <WS_ID>` | `dws drive upload` | doc import 是格式转换(docx→在线文档),drive upload 仅上传到钉盘不做转换 |
| "帮我看看知识库里的文件" | 知识库节点列表 | `wiki node list --workspace` | `drive list` | 明确"知识库"上下文 → wiki node list |
| "列出钉盘团队空间" | 列出钉盘空间 | `wiki space list --type orgSpace` | `drive list-spaces` | 空间管理归 wikidrive list-spaces 已 deprecated |
| "在知识库里搜方案" | 空间内搜索 | `wiki node search --workspace` | `drive search` | 指定了空间上下文 → wiki node search |
| "搜一下有没有叫XX的文件" | 全局搜索 | `drive search` | `wiki node search` | 未指定空间 → drive search 全局聚合搜索 |
| "我的收藏/收藏列表/收藏了哪些文档/看看收藏" | 获取收藏列表 | `drive star list` | — | 收藏管理归 drive |
| "收藏这个文档/加个收藏/标星" | 收藏文档 | `drive star add` | — | 需提供 nodeId 或 URL |
| "取消收藏/去掉收藏/不收藏了" | 取消收藏 | `drive star remove` | — | 需提供 nodeId 或 URL |
| "在知识库里创建一个文档" | 创建空文件实体 | `wiki node create --type adoc` | `doc create` | 空间内创建节点归 wiki;doc create 是向已有文档写入内容,不是创建文件节点 |
| "帮我建一个明天下午的日程" | 日历日程 | `calendar` | — | 日历日程管理(可含参与者/会议室)|
| "明早 9 点提醒我提交周报" | 创建个人待办,但需先声明 reminder 边界 | `todo` | `calendar` | todo 当前只支持 dueTime 截止时间,不支持独立精确 reminder |
| "通知群里的人都来开会" | 个人身份群发 | `chat message send` | `chat message send-by-bot` | 以个人身份向群发消息 |
| "让机器人每天推送日报" | 机器人定时推送 | `chat message send-by-bot` | `chat message send` | 需要机器人身份定期发送 |
| "CPU 超过 90% 自动告警" | Webhook 告警 | `chat message send-by-webhook` | `chat message send-by-bot` | 系统告警场景,需自定义 Webhook |
| "帮我看看收到的日报" | 收到的日志 | `report` | `doc` | 钉钉日志系统(日报/周报),不是文档 |
| "帮我创建一个待办提醒" | 个人待办 | `todo` | `report` | 个人任务提醒,不是日志汇报 |
| "拉取一下上周项目群的聊天记录" | 拉取会话消息 | `chat message list` | — | 拉取指定群聊的消息列表 |
| "看看张三发给我的消息" | 按发送者查询消息 | `chat message list-by-sender` | `chat message list --user` | 用户未明确说"单聊"时优先用 list-by-sender(跨单聊/群聊) |
| "拉取和张三的单聊记录" | 拉取单聊消息 | `chat message list --user` | `chat message list-by-sender` | 用户明确说"单聊"时用 list --user |
| "谁@了我/查看提及我的消息" | 查询@我的消息 | `chat message list-mentions` | `chat message list-all` | 都是跨会话时间范围查询,但 list-mentions 只返回@我的消息 |
| "查看我今天的所有消息" | 全量会话消息 | `chat message list-all` | `chat message list` | 用户未指定具体会话时用 list-all(跨所有会话),指定了具体群或人时用 list |
| "搜一下消息里的changefree链接" | 消息搜索 | `chat message search-advanced`(首选) | `chat search` | 推荐首选 search-advanced,它是 search 的严格超集(keyword 可选、支持多群、可叠加发送者/at 维度) |
| "按发送者搜索/指定多个群搜索/多维度搜消息" | 多维度搜索消息 | `chat message search-advanced`(首选) | `chat message search` | 推荐首选,支持关键词、发送者、@我、多个会话等维度组合 |
| "消息发没发成功/查询消息发送状态" | 查询消息发送状态 | `chat message query-send-status` | — | 需要 send 返回的 openTaskId |
| "撤回我发的消息/撤回消息/群主撤回消息/管理员撤回他人消息" | 撤回消息 | `chat message recall` | `chat message recall-by-bot` | recall 撤回个人消息或群主/管理员撤回他人消息,recall-by-bot 撤回机器人消息 |
| "编辑消息/修改已发送消息/改一下这条消息" | 编辑消息 | `chat message edit` | `chat message recall` | edit 修改已发送消息内容,推荐用 `--text` / `--title` 生成 markdown contentrecall 是撤回消息 |
| "未读消息会话/未读会话列表/我的未读会话" | 未读会话列表 | `chat message list-unread-conversations` | `chat message read-status` | list-unread-conversations 查哪些会话有未读;read-status 查具体消息的已读状态 |
| "谁看了这条消息/消息已读未读/查读状态" | 查询消息已读状态 | `chat message read-status` | `chat message list-unread-conversations` | read-status 查具体消息的已读人员;list-unread-conversations 查未读会话列表 |
| "查看消息的表情回复/拉取消息回复/消息的文字回复" | 批量拉取消息表情回复和文字回复 | `chat message list-emotion-replies` | — | 根据消息 ID 批量查询表情回复和文字回复 |
| "修改消息文字表情/更新文字表情回应" | 更新消息的文字表情回应 | `chat message update-text-emotion` | `chat message add-text-emotion` | update-text-emotion 需要原表情 ID 和新表情 IDadd-text-emotion 用于新增回应 |
| "我和风雷的共同群/我们都在哪些群" | 搜索共同群 | `chat search-common` | `chat search` | search-common 按人员搜共同群,chat search 按群名搜索 |
| "查看会话分组/自定义分组" | 获取会话分组 | `chat category list` | — | 获取用户自定义的会话分组列表 |
| "某个分组下有哪些会话" | 分组下会话列表 | `chat category list-conversations` | — | 需先通过 category list 获取分组 ID |
| "这个会话属于哪些分组/查看群所在分组" | 查看会话所属分组 | `chat category list-by-conv` | `chat category list-conversations` | list-by-conv 按会话查所属分组;list-conversations 按分组查会话 |
| "批量查分组信息/按分组ID查详情" | 批量查询分组信息 | `chat category batch-info` | `chat category list` | batch-info 按分组 ID 列表查详情;list 获取当前用户全部分组 |
| "创建会话分组/新建分组" | 创建自定义分组 | `chat category create` | — | 需指定 --title 分组名称 |
| "删除会话分组/移除分组" | 删除自定义分组 | `chat category delete` | — | 需指定 --category-id |
| "重命名分组/修改分组名称" | 更新分组名称 | `chat category rename` | — | 需指定 --category-id 和 --title |
| "把会话加到分组/将群移入分组" | 会话加入分组 | `chat category add-conv` | `chat category remove-conv` | add-conv 加入;remove-conv 移出 |
| "把会话从分组移出/从分组中删除" | 会话移出分组 | `chat category remove-conv` | `chat category add-conv` | remove-conv 移出;add-conv 加入 |
| "根据群号查群信息/群号转openConversationId" | 群号查群聊信息 | `chat group get-by-group-id` | `chat search` | 已知数字群号时直接查;用户发消息只给了群号时,先用此工具将群号转为 openConversationId |
| "我创建的群/我管理的群/我是群主的群/我当管理员的群" | 拉取我创建/管理的群 | `chat group list-my-groups` | `chat search` | list-my-groups 按角色(群主/管理员)过滤当前用户的群;chat search 按关键词搜全部群 |
| "引用消息回复/回复那条消息" | 引用回复消息 | `chat message reply` | `chat message send` | reply 引用指定消息回复;send 普通发消息不引用 |
| "转发消息/把消息转到另一个群" | 转发单条消息 | `chat message forward` | `chat message send` | forward 转发已有消息到目标会话;send 是发新消息 |
| "置顶会话/取消置顶" | 设置/取消会话置顶 | `chat set-top` | `chat list-top-conversations` | set-top 设置或取消置顶;list-top-conversations 查看置顶列表 |
| "全员禁言/群禁言" | 全员禁言或解除 | `chat group-mute` | `chat group-mute-member` | group-mute 全员禁言或解除;group-mute-member 指定成员禁言 |
| "禁言某人/指定成员禁言" | 指定成员禁言 | `chat group-mute-member` | `chat group-mute` | group-mute-member 指定成员禁言/解禁;group-mute 全员禁言 |
| "设管理员/取消管理员" | 设置群管理员 | `chat group set-admin` | `chat group invite` | set-admin 设置或取消管理员角色;invite 是邀请入群 |
| "退群/退出群聊/离开群" | 退出群聊 | `chat group quit` | `chat group members remove` | quit 是当前用户自己退群;members remove 是管理员踢别人 |
| "改群头像/更新群头像" | 更新群头像 | `chat group update-icon` | `chat group rename` | update-icon 改群头像;rename 改群名称 |
| "改群设置/群设置开关/群权限/入群许可/禁止私聊" | 更新管理员级别群功能开关 | `chat group update-settings` | `chat group set-admin` | update-settings 改管理员级别的群功能开关;set-admin 是管理员角色操作 |
| "改群昵称/设置群昵称/清除群昵称/我在群里的名字" | 设置或清除个人群昵称 | `chat group update-nick` | `chat group rename` | update-nick 改或清除自己的群昵称,不传 --nick 表示清除;rename 改群名称 |
| "群备注/给群加备注/修改群备注" | 设置群备注 | `chat group update-alias` | `chat group rename` | update-alias 设置仅自己可见的备注;rename 改群名称全员可见 |
| "我的群置顶/取消我的群置顶/置顶这个群" | 设置当前用户群会话置顶 | `chat group user-settings set` | `chat group update-settings` | user-settings set 是个人会话置顶;update-settings 是管理员级别的群功能开关 |
| "我的群免打扰/关闭我的群免打扰/这个群别提醒我" | 设置当前用户群免打扰 | `chat group user-settings set` | `chat group update-settings` | user-settings set 是个人通知偏好;update-settings 是管理员级别的群功能开关 |
| "隐藏会话/隐藏群聊/隐藏对话" | 隐藏会话 | `chat hide` | `chat mute` | hide 隐藏会话不显示;mute 是免打扰但仍显示 |
| "关闭@所有人通知/屏蔽@all/不接收@所有人" | 关闭 @所有人提醒 | `chat mute-at-all` | `chat mute` | mute-at-all 仅屏蔽 @所有人mute 是整个会话免打扰 |
| "关闭红包通知/屏蔽红包/不接收红包提醒" | 关闭红包提醒 | `chat mute-red-envelope` | `chat mute` | mute-red-envelope 仅屏蔽红包;mute 是整个会话免打扰 |
| "解散群/解散群聊" | 解散群聊 | `chat group dismiss` | `chat group quit` | dismiss 是群主解散整个群(不可逆);quit 是当前用户自己退群 |
| "普通群升级外部群/升级为外部群/转成外部群" | 将普通群升级为外部群 | `chat group upgrade-to-external` | `chat group create --type EXTERNAL` | upgrade-to-external 升级已有普通群,仅群主可执行且不可逆;create 用于新建外部群 |
| "新成员看历史/历史消息可见范围" | 设置新成员可见历史消息 | `chat group set-history` | `chat group update-settings` | set-history 控制新成员入群后可见历史消息范围;update-settings 是管理员级别的其他群功能开关 |
| "群里有哪些机器人/查看群机器人/列出群机器人" | 查看群内机器人列表 | `chat group bots` | `chat group members` | bots 只列机器人;members 列普通群成员 |
| "从群里移除机器人/踢机器人" | 移除群内机器人 | `chat group members remove-bot` | `chat group members add-bot` | remove-bot 通过 openBotId 移除;add-bot 通过 robotCode 添加 |
| "批量查群成员详情/按ID查群成员/查看指定成员信息" | 批量查询群成员详情 | `chat group members list-by-ids` | `chat group members` | list-by-ids 根据 openDingTalkId 列表查询指定成员详情;members 分页查询全部成员列表 |
| "标记未读/设为未读/会话标未读" | 标记会话为未读 | `chat mark-unread` | `chat clear-red-point` | mark-unread 标记为未读;clear-red-point 清除红点(相反操作) |
| "清除红点/已读/取消未读" | 清除会话红点 | `chat clear-red-point` | `chat mark-unread` | clear-red-point 清除单个会话红点;mark-unread 标记未读(相反操作) |
| "全部已读/一键清除所有未读/红点清零" | 清除所有会话红点 | `chat clear-all-red-point` | `chat clear-red-point` | clear-all-red-point 清除所有会话红点;clear-red-point 只清单个会话 |
| "所有会话/全部会话列表/我的会话" | 分页获取全部会话 | `chat list-all-conversations` | `chat list-top-conversations` | list-all-conversations 返回所有会话;list-top-conversations 仅返回置顶会话 |
| "清空聊天记录/删除会话消息/清空消息" | 清空会话聊天记录 | `chat clear-messages` | `chat clear-red-point` | clear-messages 清空聊天记录;clear-red-point 仅清除未读红点 |
| "标记已读/消息已读/读了" | 标记消息已读 | `chat mark-read` | `chat clear-red-point` | mark-read 标记指定消息及之前的消息为已读;clear-red-point 仅清除红点不标记消息 |
| "我加入的所有群/拉取全部群列表/我的群" | 分页拉取所有群 | `chat group list-all` | `chat group list-my-groups` | list-all 返回用户加入的所有群;list-my-groups 仅返回用户作为群主/管理员的群 |
| "入群申请记录/谁申请入群/群申请列表/查看入群审批" | 拉取入群验证记录 | `chat group list-join-validations` | `chat group list-all` | list-join-validations 拉取入群验证/审批记录;list-all 拉取群列表 |
| "通过入群申请/拒绝入群/审批入群/同意加群" | 审批入群验证 | `chat group audit-join-validation` | `chat group list-join-validations` | audit-join-validation 执行审批操作;list-join-validations 仅查询记录 |
| "发群公告/在群里发公告/群公告置顶" | 发布群公告 | `chat group notice create` | `blackboard create` | notice create 是群维度公告(发到指定群聊);blackboard 是企业公告(面向全员,不可撤回) |
| "改群公告/修改群公告/更新群公告" | 修改群公告 | `chat group notice edit` | `chat group notice create` | edit 整体替换已有公告内容(需 dataId);create 发布新公告 |
| "查群公告/看群公告/群公告列表/群里有什么公告" | 查看群公告列表 | `chat group notice list` | `blackboard list` | notice list 查指定群的公告;blackboard list 查企业公告 |
| "群公告详情/公告已读人数/谁读了公告" | 查看群公告详情 | `chat group notice get` | `chat group notice list` | get 返回单条公告详情(含已读人数、点赞/评论数);list 分页拉公告列表 |
| "搜索机器人/找机器人/查机器人/帮我找XXX机器人" | 搜索全部可用机器人 | `chat bot find` | `chat bot search` | find 返回全部可用机器人(含他人/官方),额外返回 openDingTalkId(可用于给机器人发单聊消息);search 仅返回我创建的 |
| "给机器人发单聊/给机器人发消息/跟机器人聊天" | 给机器人发单聊消息 | `chat bot find``chat message send --open-dingtalk-id` | `chat bot search` | 必须先用 find 拿 openDingTalkIdsearch 没有此字段),再用 send --open-dingtalk-id 发单聊 |
| "我创建的机器人/我的机器人/我自己的机器人/查看我的机器人" | 搜索我创建的机器人 | `chat bot search` | `chat bot find` | search 仅返回当前用户自己创建的机器人(返回 robotCode + robotName,无 openDingTalkId);find 返回全部可用机器人 |
| "合并转发/批量转发/合并转发多条消息" | 合并转发多条消息 | `chat message combine-forward` | `chat message forward` | combine-forward 合并多条为一条转发;forward 转发单条消息 |
| "转发话题/转发话题消息/话题转发到另一个群" | 转发 Thread | `chat thread forward` | `chat message forward` | thread forward 转发整条 Thread 并保留上下文;message forward 只转发普通单条消息 |
| "发卡片消息/推送流式卡片" | 创建并推送流式卡片 | `chat message send-card` | `chat message send` | send-card 发流式卡片;send 发普通文本/Markdown 消息 |
| "更新卡片/流式更新卡片" | 流式更新卡片内容 | `chat message update-card` | `chat message send-card` | update-card 更新已有卡片;send-card 创建新卡片 |
| "钉住消息/Pin消息/置顶消息到会话" | 钉住消息 | `chat message set-pin-msg` | `chat set-top` | set-pin-msg 钉住单条消息(Pin);set-top 置顶整个会话 |
| "取消钉住/Unpin消息" | 取消钉住消息 | `chat message unset-pin-msg` | `chat message set-pin-msg` | unset-pin-msg 取消钉住;set-pin-msg 设置钉住 |
| "查看钉住的消息/Pin列表/钉住消息列表" | 拉取钉住消息列表 | `chat message list-pin-msg` | `chat message list` | list-pin-msg 只返回被钉住的消息;list 拉取全部消息 |
| "置顶某条消息/把这条消息置顶" | 置顶消息 | `chat message set-top-msg` | `chat set-top` | set-top-msg 置顶会话内某条消息;set-top 置顶整个会话在列表中 |
| "取消置顶消息/取消消息置顶" | 取消置顶消息 | `chat message unset-top-msg` | `chat message set-top-msg` | unset-top-msg 取消置顶;set-top-msg 设置置顶 |
| "DING消息/查DING/DING历史" | 查询 DING 消息列表 | `ding message list` | `chat message list` | ding 是独立顶层命令;ding message list 查 DING 消息;chat message list 查普通聊天消息 |
| "DING接收状态/谁收到了DING" | DING 接收状态 | `ding message receiver-status` | `chat message read-status` | ding 是独立顶层命令;receiver-status 查 DING 接收;chat message read-status 查普通消息已读 |
| "发DING/DING通知" | 发送 DING 消息 | `ding message send` | `chat message send` | DING 是钉钉的强提醒(应用内/短信/电话),独立顶层命令;普通群消息用 chat |
| "撤回DING" | 撤回 DING 消息 | `ding message recall` | `chat message recall` | DING 撤回独立命令;chat recall 是撤回普通聊天消息 |
| "以我的名义发DING/个人发DING/用户身份DING" | 以用户身份发 DING | `ding message send-personal` | `ding message send` | send-personal 以用户身份发送,无需 robot-code;send 以机器人身份发送 |
| "以我的名义撤回DING/个人撤回DING" | 以用户身份撤回 DING | `ding message recall-personal` | `ding message recall` | recall-personal 以用户身份撤回;recall 以机器人身份撤回 |
| "消息转DING/把这条消息DING给某人/转发为DING" | 消息转 DING | `ding message send-by-message` | `ding message send-personal` | send-by-message 是将已有消息转为 DING,需指定原消息;send-personal 是直接发新 DING |
| "把最近几次关于XX的会议汇总成报告" | 按主题汇总多次听记 | #5 generate-topic-report | #7 meeting-followup | #7 是单次会议听记跟进;多次会议按主题汇总属于工作汇报 |
| "整理一下XX项目的所有讨论" | 跨源主题归档 | #5 generate-topic-report | #4 write-doc | #4 侧重单篇文档创作;按主题跨听记/群消息汇总属于工作汇报 |
| "张三在哪个部门/张三的工号是多少" | 搜人后查通讯录详情 | `aisearch person``contact user get` | 直接 `contact user search` | 姓名或工号先由 aisearch 获取 userId,再由 contact 补部门、工号等详情 |
| "研发部的详细信息/部门信息" | 查部门详情 | `contact dept get-info` | `contact dept list-members` | 查部门属性(ID、名称、人数)用 get-info;查成员列表用 list-members |
| "研发部有多少人" | 查部门人数 | `contact dept get-info` | `contact dept list-members` | 问人数用 get-info(返回 memberCount);问有哪些人用 list-members |
| "找一下张三/搜同事/找人" | 人员语义搜索 | `aisearch person` | `contact user search` | 姓名模糊搜索、工号、部门、职责和上下级走 aisearchcontact 在拿到 userId 后补详情 |
| "五道的上级是谁/谁负责XX/XX的下属有谁" | AI语义搜人 | `aisearch person` | `contact` | 涉及上下级、职责、负责人等语义维度搜索,用 aisearch |
| "222020这个工号是谁/查工号" | 按工号搜人 | `aisearch person --dimension jobNumber` | `contact` | 工号查人走 aisearchdimension=jobNumber |
| "13800138000是谁/完整手机号反查" | 精确手机号反查 | `contact user search-mobile` | `contact user search` | 完整手机号精确匹配使用 search-mobile |
| "按手机号线索找人" | 手机号语义搜人 | `aisearch person --dimension phone` | `contact user search` | 非精确手机号匹配走 aisearch 的 phone 维度 |
| "搜一下智能化方案/最近 OKR 相关邮件/最近发版相关消息" | 搜企业知识内容 | `aisearch enterprise` | `doc search` / `mail search` / `chat message search` | 跨文档、消息、日程、听记、邮件等企业内容语义检索走 enterprise;具体 `queries/types/time-range` 抽槽见 `aisearch.md` |
| "我发给某人的消息/邮件/文档/今天我干了什么" | 搜行为记录 | `aisearch behavior` | `chat` / `mail` / `doc` / `report` | 关注“谁对什么做过什么”,走 behavior;具体 `behavior-type/direction/chat-scope` 抽槽见 `aisearch.md` |
| "查/提交 请假/加班/外出/出差/补卡 审批单" | 考勤业务审批单 | `attendance approve`(查询走 `attendance approve list`;提交走 `attendance approve templates --type leave\|overtime\|repair-check\|travel\|out` | `oa approval list-pending` / `oa approval records` | 请假/加班/外出/出差/补卡 这 5 类属于考勤业务审批单,按业务类型查询;`oa approval` 是通用 OA 审批中心,覆盖范围不同 |
| "把这段文字翻译成英文/translate this" | 通用文本翻译 | `chat text translate` | `doc` / `aisearch` | 纯文本翻译,不是文档编辑或语义搜索 |
| "帮我把这个文档翻译成日文" | 文档内容翻译 | 先 `doc get``chat text translate` | `chat text translate` 直接传文件 | translate 仅支持纯文本,需先提取文档内容 |
---
## 典型场景详解
### 1. aitable vs doc vs sheet — 数据表格 vs 文档内容 vs 电子表格
**用 `aitable` 的场景**
- "创建一个表格记录团队成员信息" — 结构化数据,有行列
- "在表格里加一列'状态'字段" — 字段/列操作
- "查一下表格里所有优先级为高的记录" — 数据筛选和查询
- "用项目管理模板建一个表" — 模板创建
- 用户提到"多维表"、"Base"、"数据表"、"记录"
**用 `doc` 的场景**
- "帮我写个会议纪要" — 富文本内容创作
- "看一下这个文档链接的内容" — 阅读文档
- "在知识库创建一个文件夹" — 文档空间管理
- 用户提到"文档"、"知识库"、"写文档"
**用 `sheet` 的场景**
- "创建一个电子表格" — 创建 Excel 式在线表格
- "帮我读一下这个表格 A1 到 D10 的数据" — 按单元格区域读取
- "在 B2 写入一个 SUM 公式" — 写入公式/值到单元格
- "帮我看看这个表格有哪些工作表" — 工作表管理
- 用户提到"电子表格"、"Excel"、"工作表"、"Sheet"、"单元格"、"公式"
**三者判断关键**
- 有字段定义/记录增删改查/数据筛选 → `aitable`
- 纯文本/Markdown/富文本编辑 → `doc`
- 单元格区域读写/公式/多工作表 → `sheet`
**易误判场景**
- "在知识库中新建一个表格" — 指在钉钉文档空间创建表格类型节点 → `doc`(不是 `aitable`
- "帮我建个表记录项目进度" — 指创建结构化数据表 → `aitable`
---
### 1.1 xlsx vs axls — 本地表格文件 vs 在线电子表格
alidocs 链接表面长得一样(`https://alidocs.dingtalk.com/i/nodes/{id}`),但节点类型完全不同。sheet 产品线只服务 axls(在线电子表格),xlsx / xls / xlsm / csv 等本地表格文件必须走 `dws drive download`,严禁错路由。
`sheet` 的场景(axls,钉钉在线电子表格):
- `dws drive info --node <URL>` 返回 `extension=axls`
- 用户在钉钉文档空间直接"新建电子表格"得到的节点
- 所有 sheet 子命令(`list` / `range read` / `range write` / `export` 等)仅服务这类节点
`dws drive download` 的场景(xlsx / xls / xlsm / csv 本地表格文件):
- `dws drive info --node <URL>` 返回 `extension=xlsx` / `xls` / `xlsm` / `csv`
- 用户把本地 Excel 文件上传到文档空间得到的节点,本质是"文件 + 预览",非在线表格
- sheet 命令直接调用会报错,必须先 `dws drive download --node <URL>` 下载到本地再解析处理
判断关键:
- 未知 alidocs URL → 必须先 `dws drive info --node <URL> --format json` 探测 `extension`
- `extension=axls``sheet`
- `extension=xlsx` / `xls` / `xlsm` / `csv``dws drive download`
- 用户说"把在线表格导出为 xlsx 文件" → `dws sheet export`(axls → xlsx 的格式转换,不是读取 xlsx)
易误判场景:
- 用户粘贴一个 alidocs 链接说"读一下这个表格" — 不能直接调 `sheet range read`,必须先 probe 再按 `extension` 路由
- 用户说"读一下这个 xlsx 文件里的数据" — 走 `dws drive download` 下载后本地解析,不要走 `sheet`
- 用户说"把这个在线表格导出为 xlsx" — 走 `dws sheet export`,不要走 `dws drive download`(后者只能下载已有的 xlsx 节点,无法从 axls 生成)
详见 [url-patterns.md](./url-patterns.md) 和 `sheet.md 适用范围`(详见 `dingtalk-misc``references/sheet.md`)。
---
### 2. devdoc vs drive search / wiki node search — 两种搜索
**用 `devdoc` 的场景**
- "API 调用报错 403 怎么解决" — 开发调试问题
- "搜一下 OAuth2 接入文档" — 开放平台技术文档
- "CLI 命令出错了怎么办" — CLI 使用错误
- 用户提到"开发"、"API"、"调用错误"
**用 `drive search` / `wiki node search` 的场景**
- "在我的文档里搜一下'项目方案'" — 全局搜索用 `drive search`
- 用户明确说"某个知识库里搜" — 空间内搜索用 `wiki node search --workspace`
**判断关键**:搜开发文档→ `devdoc`;搜用户自己的文档→ `drive search`(全局)或 `wiki node search`(空间内)
---
### 3. drive vs doc vs wiki — 存储层 vs 内容层 vs 空间管理层
> **三层模型判定口诀**:如果操作换一种文件类型还能成立,就是存储层(→ drive);操作只对特定格式有意义,就是内容层(→ doc/sheet);操作是对空间/节点的组织管理,就是空间管理层(→ wiki)。
**用 `drive`(存储层)的场景**
- "把这个 PDF 传到钉盘" — 上传文件(不关心格式)
- "用本地 PDF 覆盖钉盘里的文件" — 覆盖已有文件实体(不关心格式)
- "下载那个 Excel 附件" — 下载文件(不关心格式)
- "看一下钉盘根目录有什么文件" — 浏览文件列表
- "搜一下有没有叫季度汇报的文件" — 全局搜索文件实体 (`drive search`)
- "把这个文档复制一份" — 复制文件实体
- "把文件移到另一个文件夹" — 移动文件实体
- "改一下文件名" — 重命名文件实体
- "给张三加个编辑权限" — 权限管理
- 用户提到"钉盘"、"网盘"、"上传"、"下载"、"搜文件"、"找文件"、"复制"、"移动"、"重命名"、"权限"
**用 `doc`(内容层)的场景**
- "读一下这个文档的内容" — 读取文档 Markdown(仅 adoc 有意义)
- "帮我写入一段话到文档里" — 编辑文档内容(仅 adoc 有意义)
- "在第三段后面插入一个表格" — 块级编辑(仅 adoc 有意义)
- "给这段内容加个评论" — 文档评论(仅 adoc 有意义)
- "把这个文档导出为 docx" — 文档导出(当前仅 adoc 支持)
- 用户提到"读文档内容"、"写文档"、"编辑文档"、"块级编辑"、"文档评论"、"导出文档"
**用 `wiki`(空间管理层)的场景**
- "列出所有知识库" — 空间列表 (`wiki space list`)
- "列出钉盘团队空间" — 钉盘空间列表 (`wiki space list --type orgSpace`)
- "在产品知识库里搜一下方案" — 空间内搜索 (`wiki node search --workspace`)
- "在知识库里创建一个空白文档" — 创建节点 (`wiki node create --type adoc`)
- "把这个文件移到另一个知识库" — 跨知识库移动 (`drive move`)
- "给知识库加个成员" — 成员管理 (`wiki member add`)
- 用户提到"知识库"、"团队空间"、"空间成员"、"空间内搜索"、"列出空间"
**判断关键(三层模型)**
- 操作不关心文件格式 → `drive`(存储层)
- 操作仅对特定文档格式有意义 → `doc` / `sheet`(内容层)
- 操作是对空间/节点的组织管理 → `wiki`(空间管理层)
**搜索场景路由**
- "搜文件" / "找文件"(不指定空间) → `drive search`(全局聚合搜索)
- "在某个知识库里搜" → `wiki node search --workspace <id>`(空间内搜索)
**创建场景路由**
- "在知识库里创建一个文档" → `wiki node create --type adoc`(创建空文件实体)
- "帮我写一篇项目周报" → `doc create`(创建并写入内容)
- "创建一篇文档并写入内容" → 先 `wiki node create``doc update`(先创建实体,再写内容)
**列表场景路由**
- "列出钉盘文件" → `drive list`
- "列出知识库里的文件" → `drive list --workspace <id>``wiki node list --workspace <id>`
- "列出所有空间" → `wiki space list`
---
### 4. 视频会议已下线 — 一律走 calendar
视频会议产品已从当前开源 CLI 下线,发起会议、邀请入会、会中控制请在钉钉客户端操作。涉及"开会/约会议"的诉求按日程处理:
- "明天下午安排个会" — 日程管理(可含会议室),`calendar event create`
- "给自己留两个小时写方案/建个个人日程" — 个人日历事件,仍用 `calendar event create`
- "帮我约几个人开会" — 创建日程 + 添加参与者
- "看看下午有没有空闲会议室" — 会议室管理
- "帮我查一下同事有空吗" — 闲忙查询
- "发起视频会议/共享屏幕/静音/邀请入会" — CLI 已下线,引导用户在钉钉客户端操作;如需预约时间改用 `calendar event create`
---
### 5. chat 内部 — 消息发送与撤回
**用 `chat message send` 的场景**
- "帮我在群里发个消息提醒大家" — **个人身份**发群消息
- "发个单聊消息给某人" — 个人身份发单聊:
- 已有 userId 时直接使用 `--user`;已有 openDingTalkId 时使用 `--open-dingtalk-id`
- `+messages-send --user` 对所有内容类型都会通过通讯录关键词搜索并按 userId 精确匹配 openDingTalkId;无需手动预查,`--dry-run` 也会执行这次只读解析
- 已持有 openDingTalkId 时优先使用显式 `--open-dingtalk-id`,避免额外解析
- "发张图片/截图/语音/视频/文件到群里" / "发张图给某某" — **统一一条命令**`dws chat message send ... --msg-type file --file <本地路径>`,CLI 内部自动上传并发送,**任意扩展名(png/jpg/pdf/mp4/zip…)都走这条**
- "发图片+文字说明" — 不要硬塞进一条命令;先发文件消息再补一条 `--content "..."` 即可
```bash
dws chat message send --conversation-id <openConversationId> --msg-type file --file ./screenshot.png --format json
dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file ./report.pdf --format json
```
> ❌ 反模式:调 `dt_media_upload` / `extract_media_id.py` / `drive upload` / `drive download` 等前置工具再 `--msg-type image --media-id`。这是**旧链路**,仅当上游已持有 mediaId 才用;新场景一律 `--file` 直发,避免长链路与“空白图”现象。
> 单聊已持有 openDingTalkId 时优先使用 `--open-dingtalk-id`;传 `--user` 时 CLI 会通过通讯录关键词搜索做 userId 精确匹配后发送。
**用 `chat +messages-send-card` 的场景**
- 群聊流式卡片使用 `--group <openConversationId>`
- 单聊已有 userId 时使用 `--receiver <userId>`,CLI 始终通过通讯录关键词搜索并按 userId 精确匹配 openDingTalkId;即使 userId 以 D/d 开头也不会猜测类型,`--dry-run` 也会执行该解析。
- 单聊已有 openDingTalkId 时必须显式使用 `--receiver-open-dingtalk-id <openDingTalkId>`,避免与 userId 混淆。
- `--group``--receiver``--receiver-open-dingtalk-id` 严格三选一;传 `--content` 可在同一次调用中创建并结束卡片。
- "发送位置/坐标/地址到群里" / "发个位置给某某" — `dws chat message send ... --msg-type location --latitude <纬度> --longitude <经度> --location-name <地址名称> --map-thumbnail-url @mediaId`;地图缩略图需先通过 `dt_media_upload` 上传获取 mediaId
```bash
dws chat message send --conversation-id <openConversationId> --msg-type location --latitude <纬度> --longitude <经度> --location-name <地址名称> --format json
```
**用 `chat message send-by-bot` 的场景**
- "让机器人在群里发一条通知" — **机器人身份**发消息
- "给张三发一条机器人单聊消息" — 机器人单聊
**用 `chat message send-by-webhook` 的场景**
- "通过 Webhook 发告警到群里" — 自定义机器人 Webhook
- 用户有 Webhook Token
**用 `chat message recall-by-bot` 的场景**
- "撤回刚才机器人发的消息" — 需要 robot-code + processQueryKey
`chat message recall` 的场景:
- "撤回我刚发的消息" — 撤回以个人身份发送的消息,需要 openConversationId + openMessageId
`chat message query-send-status` 的场景:
- "消息发没发成功/查询消息发送状态" — 查询个人发送消息的状态,需要 send 返回的 openTaskId
`chat message search-advanced` 的场景(推荐首选):
- "按发送者搜索消息/指定多个群搜索/@我的消息多维度搜" — 支持关键词、发送者、@我@指定人、多个会话等维度组合搜索
- 替代关系:完全替代 `chat message search`(严格超集:keyword 可选 vs 必填,支持多群 vs 单群);大部分替代 `chat message list-by-sender`--user/--users 覆盖按 userId 搜索发送者,--sender-ids 覆盖按 openDingTalkId 搜索)和 `chat message list-mentions`--at-me 覆盖核心功能)
- 不能替代:`chat message list-focused`(「特别关注人」是独立维度)
- 默认使用 search-advanced,仅在上述不适用场景才降级到具体命令
**不支持的场景**
- "撤回我刚发的消息"(但不知道消息 ID) — 需先通过消息拉取或搜索接口(如 `chat message list``chat message search-advanced` 等)获取 openMessageId,再调用 `chat message recall`
判断关键:个人发→ `send`;机器人发→ `send-by-bot`;有 Webhook Token→ `send-by-webhook`;个人撤回→ `recall`;机器人撤回→ `recall-by-bot`;查发送状态→ `query-send-status`;消息搜索类意图优先路由到 `search-advanced`(推荐首选),仅在不适用时降级到具体命令
---
### 6. chat — 群聊会话
**用 `chat` 的场景**
- "在群里发条通知" — 钉钉会话/群消息
- "拉取某个群/某个人单聊的聊天记录" — `chat message list`
- "某人发给我的消息" — `chat message list-by-sender`
- "@我的消息/提及我的" — `chat message list-mentions`
- "查看我最近的所有消息" — `chat message list-all`
- "特别关注人的消息/关注的人的消息/我特别关注的人最近发了什么消息/关注的人最近聊了啥" — `chat message list-focused`
- 注:判断顺序——**先**看 query 是否含动词【发/说/聊/讲】或名词【消息/聊天/动态】,含则路由到 `chat message list-focused`**仅**当 query 终点是"人员列表"(如"我关注了谁/我特别关注的人有哪些",无任何消息域动词)时,才路由到 `contact relation list-my-followings`
- "搜索消息里的XX/查找包含XX的消息" — `chat message search`
- "我和XX的共同群" — `chat search-common`
- "置顶会话/我的置顶/查看置顶" — `chat list-top-conversations`
- "置顶消息" — 先 `chat list-top-conversations` 拉置顶会话列表,再用 `chat message list --group <openConversationId>` 分别拉各会话的消息
- "置顶某条消息/把这条消息置顶" — `chat message set-top-msg`
- "取消置顶消息/取消消息置顶" — `chat message unset-top-msg`
- "设置/取消会话置顶" — `chat set-top`--off 取消置顶)
- "引用回复消息" — `chat message reply`
- "转发消息到另一个群" — `chat message forward`
- "全员禁言/解除禁言" — `chat group-mute`--off 解除禁言)
- "禁言某人/指定成员禁言" — `chat group-mute-member`
- "设管理员/取消管理员" — `chat group set-admin`--off 取消)
- "发群公告/定时发群公告" — `chat group notice create`(--run-at 定时发布;企业级全员公告用 `blackboard create`
- "改群公告" — `chat group notice edit`(需先用 notice list 拿 dataId
- "查群公告/群公告列表/定时公告" — `chat group notice list`--scheduled 查定时公告)
- "群公告详情/公告已读人数" — `chat group notice get`
- "发DING/DING通知" — `ding message send`(机器人身份,需 --robot-code
- "以我的名义发DING/个人发DING/用户身份DING" — `ding message send-personal`(用户身份,无需 robot-code
- "撤回DING" — `ding message recall`(机器人身份)
- "以我的名义撤回DING/个人撤回" — `ding message recall-personal`(用户身份)
- "DING消息/查DING历史" — `ding message list`
- "DING接收状态/谁收到了DING" — `ding message receiver-status`
- 用户明确说"群"、"会话"、"机器人发群消息"、"Webhook"
---
### 7. report vs doc vs todo — 日志 vs 文档 vs 待办
**用 `report` 的场景**
- "帮我看看收到的日报" — 收件箱列表 (`report inbox list`)
- "帮我写/提交今天的日报(钉钉日志模版)" — 先 `report template list` / `template get`,再 `report entry submit`
- "有什么日志模版" — 查看模版 (`report template list`)
- "看看这个日志的已读统计" — 阅读状态 (`report entry stats`)
- "我发过的日志有哪些" — 发件箱列表 (`report outbox list`)
- 用户提到"日报"、"周报"、"日志"
**用 `doc` 的场景**
- "帮我写个项目总结文档" — 长文本创作(钉钉在线文档,非日志模版)
**用 `todo` 的场景**
- "记一下这周要做的事" — 个人任务管理
- "创建一个待办提醒" — 仍归 `todo`,但要先说明当前只有 dueTime 截止时间,没有独立 reminder schedule
**判断关键**:钉钉日志系统(日报/周报模版,含按模版创建汇报)→ `report`;文档/知识库长文→ `doc`;任务清单→ `todo`
---
### 7.1 attendance approve vs oa approval — 考勤业务审批 vs 通用 OA 审批
**用 `attendance approve` 的场景**(考勤业务审批单):
- "上周谁请假了 / 某人近期的加班记录 / 外出出差单 / 补卡审批单" — `attendance approve list --types overtime,leave,trip,patch``trip` 在查询接口 bizType=2 同时覆盖出差与外出,两者合并为同一类,查询不再细分;travel / business_trip / 出差 / 外出 亦映射到 2)
- "查看考勤审批模板 / 帮我提交考勤审批" — `attendance approve templates --type leave|overtime|repair-check|travel|out`(外出=travel/TRAVEL,出差=out/trip/OUT
- 用户提到“请假单 / 加班单 / 出差单 / 补卡单 / 考勤审批”等考勤上下文
**用 `oa approval` 的场景**(通用 OA 审批中心):
- "我的待审批 / 待办审批 / 已审批记录" — `oa approval list-pending`
- "某审批单详情 / 同意或驳回审批 / 撤销我发起的审批" — `oa approval detail/approve/reject/revoke`
- "查业务审批记录 / 审批转交 / 添加评论与抄送" — `oa approval records/transfer/comment/cc`
- 用户提到“报销 / 采购 / 用印 / 合同 等非考勤类审批”
**判断关键**
- 审批主题明确是【请假 / 加班 / 外出 / 出差 / 补卡】这 5 类 → `attendance approve`
- 不限于考勤业务、面向“我上下游的审批任务”表述 → `oa approval`
**提交审批单的边界**
- 提交考勤审批单走 `attendance approve templates --type leave|overtime|repair-check|travel|out`(外出=travel,出差=out/trip),命令会返回审批表单的 submitUrl 跳转链接,由用户点击链接跳转到钉钉客户端的提交页面完成填写与提交。**展示链接时必须用 Markdown 可点击格式 `[表单名称](submitUrl)`,不要裸露 URL**。
- 提交诉求的辅助查询:可用假期余额走 `attendance vacation balance`、历史已提交记录走 `attendance approve list`
- 任何场景下都**不要误用 `oa approval` 代替** —— 该命令组只能查/审/撤已存在的审批单,考勤业务审批单走考勤自己的逻辑便于区分。
---
## 跨产品工作流路由
以下场景需要多个产品配合完成,注意上下文传递顺序。多步骤操作有现成脚本时优先使用脚本。
### 发邮件给同事(aisearch → contact → mail
用户说“给张三发封邮件”,但只知道名字不知道邮箱地址:
> 有脚本: 加载 `dingtalk-mail` sub-skill 后使用其邮件发送辅助脚本;该脚本不在 `dingtalk-shared` 包内。
```bash
# 1. 搜人获取 userId(多人同名须 contact user get 消歧,禁止默认选第一个,详见 08-directory.md「多命中」)
dws aisearch person --query "张三" --dimension name --format json
# 2. 用 userId 查详情获取 email
dws contact user get --ids <userId> --format json
# 3. 用搜索到的邮箱地址作为收件人发送邮件
dws mail mailbox list --format json # 获取发件人邮箱
dws mail message send --from my@company.com --to zhangsan@company.com \
--subject "周报" --body "内容" --format json
```
### 创建日程并邀请同事(aisearch → calendar
用户说“约张三明天下午开会”:
> 有脚本: 加载 `dingtalk-calendar` sub-skill 后使用其日程创建辅助脚本;该脚本不在 `dingtalk-shared` 包内。
```bash
# 手动流程(脚本不可用时):
# 1. 搜人获取 userId(多人同名须 contact user get 消歧,禁止默认选第一个,详见 08-directory.md「多命中」)
dws aisearch person --query "张三" --dimension name --format json
# 2. 创建日程
dws calendar event create --title "会议" \
--start "2026-03-15T14:00:00+08:00" --end "2026-03-15T15:00:00+08:00" --format json
# 3. 添加参与者
dws calendar participant add --event <EVENT_ID> --users <USER_ID> --format json
```
### 创建待办并指派(aisearch → todo
用户说“给张三建个待办”:
```bash
# 1. 搜人获取 userId(多人同名须 contact user get 消歧,禁止默认选第一个,详见 08-directory.md「多命中」)
dws aisearch person --query "张三" --dimension name --format json
# 2. 创建待办
dws todo task create --title "任务内容" --executors <USER_ID> --format json
```
---
### 8. 纯通讯录查询 vs 跨产品(#8)
**仅查人/部门/成员/归属/组织关系**(没有「发消息、写文档、建待办」等第二动作)→ 匹配 [SKILL.md](../SKILL.md) **#8 通讯录**(行动指南 `dingtalk-contact/references/08-directory.md`),不要用 #5 汇报或 #4 文档。
**多轮对话**:用户先说「搜 userId」再说「要详细资料」→ 仍属 #8;第二步 **必须** 执行 `contact user get --ids`,禁止只用第一次 `aisearch person` 的浅表字段交差。
**与 #1 消息区分**:终点是「把消息发给某人」→ #1;终点是「某人 userId/部门是什么」→ #8。可先 #8 解析 ID 再 #1
**与发邮件、待办、日程混排**:先后顺序与口径见 `dingtalk-contact/references/08-directory.md`
**特别关注列表查询**:用户说"我关注了谁/我的特别关注列表/我的星标联系人/特别关注的人有哪些" → `dws contact relation list-my-followings`(无入参)。
-`chat message list-focused` 区分:前者拉"人员列表",后者拉"这些人发的消息"。
- **可执行判断口径(按顺序扫描)**:
1. 扫描 query 是否含动词【发/说/聊/讲】或名词【消息/聊天/动态/最新内容】 → 含则**强制**路由到 `chat message list-focused`,**忽略**主语中的"关注/特别关注/星标"。
2. 仅含"关注/特别关注/星标"+"人/列表/谁/有哪些/多少" → 路由到 `dws contact relation list-my-followings`
- **反例 query(绝不路由到 list-my-followings**
- "我特别关注的人最近发了什么消息" → `chat message list-focused`
- "关注的人最近都说了啥" → `chat message list-focused`
- "星标联系人发的群消息" → `chat message list-focused`
- **正例 query(路由到 list-my-followings**
- "我特别关注的人有哪些"、"我关注了谁"、"我的星标联系人"
### 发送图片 / 本地文件(统一一条命令)
用户说"发张图/把这张图发给某某/发个 PDF/发个语音/发个视频/发个文件"等任何场景,**统一一条命令**:
```bash
# 群聊
dws chat message send --conversation-id <openConversationId> --msg-type file --file <本地路径> --format json
# 单聊(推荐 --open-dingtalk-id--user 也支持)
dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file <本地路径> --format json
```
支持任意扩展名(`.png/.jpg/.gif/.bmp/.webp/.pdf/.doc/.xls/.zip/.mp3/.wav/.mp4/.avi` …),CLI 自动识别并处理。**无需** `dt_media_upload` / `extract_media_id.py` / `drive upload` / `drive download` / `chat conversation-info` / `chat file upload` 等任何前置工具调用。
### 图片/文件 + 文字说明
不要把文字塞进 `--msg-type file` 命令(该命令不读 `--content`)。先发文件再补一条文本消息即可:
```bash
dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file ./screenshot.png --format json
dws chat message send --open-dingtalk-id <openDingTalkId> --content "这是本周数据汇总" --format json
```
### 旧链路(mediaId)— 仅兼容场景
仅当上游已经通过 `dt_media_upload` 拿到 `@lQL...` 形式的 mediaId 时使用:
```bash
dws chat message send --conversation-id <openConversationId> --msg-type image --media-id "@lQLPD4JNnliqBq3NBQDNA8Cw" --format json
```
@@ -0,0 +1,45 @@
# 通用规范
> full recipe 执行时的共享规范。安全门控、危险操作确认、`--format json` 等已在根 [SKILL.md](../../../SKILL.md) 中定义,此处不重复。
> Recipe 元规范(YAML frontmatter、命名、三层架构)见 [meta.md](meta.md)。
## 批量查询规范
| # | 规范 |
|---|------|
| 1 | **并行查详情**:拿到多个 ID 后,用 `&` 合并到同一条 Shell 命令并行执行 + `wait`**严禁逐条串行** |
| 2 | **翻页**:分页接口须拉全直至无更多 |
| 3 | **优先批量 API**:有批量接口则用批量;无则按 #1 并行 |
| 4 | **群消息**:必须先 `chat search --query``openConversationId`,再 `chat message list --group <openConversationId> --time "<yyyy-MM-dd HH:mm:ss>" --direction older`;多群同条命令并行 |
| 5 | **列表少轮次**:带条件搜索/列表 → 一次采全详情;**禁止**无新参数时重复同一 `list` / `search` |
## 多源并行采集(公共模式)
> recipe 引用方式:`按「多源并行采集」执行(关键词=<X>,时间=<Y>至<Z>`。
- 同条 Shell`&` 并行 + `wait`;分页须采全。
- 只保留与主题相关的数据,无关丢弃。
- 有批量详情接口优先;否则并行拉详情(见上表 #1)。
- 具体采哪些产品列表由对应 **行动指南 recipe** 与 [SKILL 产品参考](../../../SKILL.md) 决定。
## 字段术语与 ID 传递
> list 返回 JSON 后,必须提取下表字段传给后续命令。**禁止用其他字段替代。**
| 字段 | 来源 | 传递给 |
|------|------|--------|
| `taskUuid` | `minutes list` | `minutes get summary/info/batch --id(s)` |
| `userId` | `aisearch person` / `contact user search` / `contact dept list-members` | `contact user get --ids``todo --executors``calendar --users` |
| `deptId` | `contact dept search` | `contact dept list-members --ids <deptId1,deptId2...>`;多子部门时对每个子部门分别 `dept search` 取 id |
| `nodeId` | `drive search` / `wiki node search` | `doc read/update --node``drive copy/move/rename/delete --node` |
| `nodeId` | `wiki node list` 中的 folder 类型节点 / `wiki node create --type folder` | `wiki node list --folder``wiki node create --folder``drive upload --folder``drive copy/move --folder` |
| `eventId` | `calendar event list` | `calendar event get/update --id` |
| `processInstanceId` | `oa approval list-*` | `oa approval detail/approve --instance-id` |
| `openConversationId` | `chat search` | `chat message list/send --group` |
| `todoTaskId` | `todo task list` | `todo task update/done --task-id` |
| `reportId` | `report inbox list` / `report outbox list` | `report entry get/stats --report-id` |
| `baseId` / `tableId` | `aitable base search` | `aitable record query --base-id --table-id` |
| `dentryUuid` | `drive list` / `drive mkdir` | `drive info/download/copy/move/rename/delete --node``drive list/mkdir/upload/copy/move --folder` |
| `dentryId` | `drive info` 的数字字段 | 仅用于 `chat message send --dentry-id` |
**ID 边界硬约束**`dentryId` 通常是纯数字,只表示聊天文件消息需要的钉盘条目数字 ID;它不是父目录 ID。遇到 `drive --node/--folder``doc --node``wiki node --folder` 时,只能使用 `dentryUuid` / `nodeId` / 文档 URL。若当前上下文只有数字型 `dentryId`,必须先重新 `drive list` / `drive search` / `wiki node list` 获取正确 ID,不能把该数字直接代入后续命令。
@@ -0,0 +1,275 @@
# Lite Recipe 完整步骤
> 核心流程步骤 3 判定为 lite 后,按本文件中对应 recipe 的步骤**直接执行**。
> 所有命令均须加 `--format json`(下文省略)。
## #1 消息沟通
所有消息沟通相关的命令详情、参数说明、意图路由和复合工作流,请查阅 `dingtalk-chat`
## #2 任务管理
### create-todo
1. 确定执行者:指定姓名 → `aisearch person --query "<姓名>" --dimension name``userId`;未指定 → `contact user get-self``userId`;多人 → 逐个搜索逗号拼接。
2. 创建:`todo task create --title "<标题>" --executors <userId>[,<userId2>...] --priority <优先级>`(可选 `--due "<截止ISO>"`)→ `todoTaskId`
### todo-query-ops
- 查询:`todo task list [--status false|true]`(不传=全部)
- 详情:`todo task get --task-id <id>`
- 完成/重开:`todo task done --task-id <id> --status <true|false>`
- 按主题筛选:list 后按标题关键词过滤
## #3 会议日程
### list-today-meetings
**优先**:加载 `dingtalk-calendar` sub-skill 后按其「今天/明天/本周日程」recipe 执行;该自动化脚本只在 `dingtalk-calendar` 包内可用。
备选:`dws calendar event list --start "<今日起始ISO>" --end "<今日结束ISO>"`(须加 `--format json`
### check-users-busy
查询多人在某时段内的闲忙(**busy**,不是用 `event list` 扫日程):
1. 解析用户:对每个姓名执行 `aisearch person --query "<姓名>" --dimension name``userId`;多人将 `userId` 用英文逗号拼接(无空格或按 `dingtalk-calendar` `busy search` 要求)。
2. 确认时段:用户须给出或可收敛为明确的 `--start` / `--end`(ISO-8601);若未给出,**先追问**起止时间,禁止用任意默认全天窗口代替用户意图。
3. 执行:`dws calendar busy search --users <userId1,userId2,...> --start "<ISO>" --end "<ISO>" --format json`
详见 `dingtalk-calendar` 中「查询用户闲忙状态」。
## #4 文档知识
### query-doc
1. 全局搜索:`drive search --query "<关键词>"``nodeId`(聚合钉盘+文档空间)
2. 空间内搜索:`wiki node search --workspace <WS_ID> --query "<关键词>"``nodeId`
3. `doc read --node <nodeId>`(按需;大文档只抽章节)
### list-folder-docs
`drive list --workspace <WS_ID>``wiki node list --workspace <WS_ID>`
## #5 工作汇报
### query-report-list
1. 收到的日志:先把用户时间词转成起止时间,再执行 `report inbox list --start "<YYYY-MM-DDT00:00:00+08:00>" --end "<YYYY-MM-DDT23:59:59+08:00>" --cursor 0 --size 20 --format json`。用户只说“最近/近期/最近收到”时默认最近 7 天。
2. 我发过/我创建的日志:首条查询必须用 `report outbox list --cursor 0 --size 20 --format json`;如用户指定时间,补 `--start "<YYYY-MM-DDT00:00:00+08:00>" --end "<YYYY-MM-DDT23:59:59+08:00>"`
3. 按发件人过滤收件箱:先 `aisearch person --query "<姓名>" --dimension name --format json``userId/staffId`,再加 `--sender-user-ids <id>`;空结果必须说明未找到该发件人的日志,不得改选其他人。
4. 面向用户时必须基于 `result[]` 拼 Markdown 表,表头固定为 `日期 | 标题 | 发送人 | 状态 | 钉钉链接`;每条 `result[]` 都会带这五个中文字段,不要把 `reportId` / `日志ID` 作为主列。
5. 用户要正文、详情、统计、汇总或总结多篇日志时,必须用内部保留的 `reportId` 逐篇执行 `report entry get --report-id <reportId> --format json``report entry stats --report-id <reportId> --format json`;选前 5 篇时调用次数应等于实际选中篇数。
时间 flag 硬约束:只允许 `--start` / `--end`;禁止 `--start-date` / `--end-date` / `--date`。不要只传 `2026-05-04`,必须展开成 `2026-05-04T00:00:00+08:00` 这种完整 ISO;禁止 UTC `Z` / `date -u`
硬约束:`report inbox list` 是收到的日志(别人发给我),`report outbox list` 是我创建/发出的日志(我发给别人)。不要混淆方向;不要回答"API 不支持收到的日志"。
> 旧命令兼容:`report list` / `report inbox` / `report sent` / `report created` / `report detail` / `report stats` 仍可执行,但已 deprecated,stderr 会打废弃提醒,新计划一律使用 `inbox list` / `outbox list` / `entry get` / `entry stats`。
禁止:不要先查 help,不要为了格式化列表创建脚本;不要传 `--size 50/100``report inbox` 可作为兼容入口使用,但新计划优先写规范命令 `report inbox list --start "<YYYY-MM-DDT00:00:00+08:00>" --end "<YYYY-MM-DDT23:59:59+08:00>" --cursor 0 --size 20 --format json`
### check-report-read-status
`report entry stats --report-id <reportId>` → 已读/未读
## #7 听记与会后
> 产品命令完整参考见 `dingtalk-minutes`。full recipe 见 `dingtalk-minutes/references/07-minutes.md`。
### minutes-query(查询与获取)
> **scope 选择铁律(P2 真实 badcase**`list` 后的 scope 决定查询范围,最高频误判是把"我能访问的所有听记"错选成 `mine`
> - `mine` = **仅我自己创建/发起**的听记(范围最窄)
> - `shared` = **仅他人共享给我**的听记
> - `all` = **我可访问的全部**= mine shared,范围最广)
> - **判定口诀**query 含"访问/权限/可见/能看到/所有/我的"等覆盖范围语义 → 一律走 `all`;**仅当**明确说"我创建的/我发起的/我录的" → 才走 `mine`。**不要因为句子里有"我"字就退化成 `mine`。**
> - 错误:`我能访问的所有听记` → `list mine`(漏掉共享给我的,判定不通过)
> - 正确:`我能访问的所有听记` → `dws minutes list all --format json`
> **选对象铁律(0605 P2 EDD badcase 提炼,命令对了但选错听记 = 整任务失败)**:list/搜索拿到结果后,必须按语义精准锁定目标听记,详见 `dingtalk-minutes`「选对象铁律 S1~S6」。速记:
> - **S1 跨组织汇总**:以 list 返回的 `taskUuid + title + organizationName` 三元组为准逐条照抄,组织与听记不可张冠李戴。
> - **S2 "最近一次某类会议"**:先 `--query "<主题词>"`(如周会)过滤出该类,再在候选里取时间最新;主题匹配优先级高于时间。
> - **S3 比时长最长**:必须读 `durationMicros` 字段做数值比较,禁止凭印象/标题猜,口头结论与操作的 taskUuid 须自洽。
> - **S4 内容为空**:锁定 taskUuid 后所有 get/update 复用同一 id;某字段为空就如实说,**禁止偷偷切换到另一条听记**。
> - **S5 模糊日期匹配不到**:日期可能是"会议主题日期"而非"创建日期",按标题关键词搜,精确日期没命中就放宽 ±7 天/同主题候选请用户确认,**禁止直接报"找不到"**。搜索回退策略:① 先 `--query "<主题关键词>"` 不带日期搜 → ② 若结果过多则加 `--start/--end` 扩大到 ±7 天 → ③ 列出候选让用户确认。
> - **S6 给标题没给 id**:必须先 `list all --query "<标题关键词>"` 定位 taskUuid 再 update/get,禁止凭记忆直接填 `--id` 跳过定位。
**列表查询**`list` 后**必须**跟 scope`mine`/`shared`/`all`,默认补 `all`):
```bash
# 我可访问的所有听记(默认)
dws minutes list all --format json
# 按关键词服务端搜索(严禁全量拉取后本地 grep)
dws minutes list all --query "周会" --format json
# 按时间范围筛选(ISO-8601 格式)
dws minutes list mine --start "2026-05-01T00:00:00+08:00" --end "2026-05-25T23:59:59+08:00" --format json
# 关键词 + 时间组合
dws minutes list all --query "需求评审" --start "2026-05-25T00:00:00+08:00" --end "2026-05-25T23:59:59+08:00" --format json
# 限制条数
dws minutes list mine --limit 5 --format json
# 共享给我的听记
dws minutes list shared --query "ROI" --format json
```
| 参数 | 说明 |
|------|------|
| `--query "<关键词>"` | 服务端关键词搜索 |
| `--start "<ISO-8601>"` | 开始时间 |
| `--end "<ISO-8601>"` | 结束时间 |
| `--limit <N>` | 每页条数,默认 10`--max` 为兼容别名) |
| `--cursor "<token>"` | 分页 token,首页留空(`--next-token` 为兼容别名) |
**获取详情**
- 批量基础信息:`minutes get batch --ids <uuid1,uuid2,...>`
- 单篇摘要:`minutes get summary --id <taskUuid>`
- 转写原文(自动翻页):`minutes get transcription --id <taskUuid>`(返回 `cursor` / `nextToken` / `nextCursor` 时用 `--cursor <token>` 继续)
- 关键词:`minutes get keywords --id <taskUuid>`
- 待办事项:`minutes get todos --id <taskUuid>`
- 基础信息:`minutes get info --id <taskUuid>`
- 音频地址:`minutes get audio --id <taskUuid>`
> `--id`/`--uuid`/`--task-uuid` 三者等价。推荐 `--id`。
### minutes-edit(编辑与替换)
- **替换转写文字**`minutes replace-text --id <taskUuid> --search "旧文字" --replace "新文字"`
- 执行前检查特殊字符(引号/书名号/括号等),若包含先提示用户确认去除
- 替换成功后追问是否加热词:`minutes hot-word add --words "新文字"`
- **替换发言人**:先统一搜人取得 dingUid → `minutes speaker replace --id <taskUuid> --from "发言人X" --to "姓名" --target-uid <userId>`
- 查询 dingUid`aisearch person --query "姓名" --dimension name --format json` → 取 `userId`
- 多个匹配 → 列出候选让用户选;无匹配 → 不带 `--target-uid` 执行
- **修改标题**`minutes update title --id <taskUuid> --title "新标题"`
- **修改摘要**`minutes update summary --id <taskUuid> --content "新内容"`
- **热词管理**`minutes hot-word add --words "词1,词2"` / `minutes hot-word list`
- **思维导图**`minutes mind-graph create --id <taskUuid>``mind-graph status --id <taskUuid>` 轮询至完成
### minutes-tag(标签/分组查询)
- 查询标签列表:`minutes tag list` → 返回用户在听记页面创建的所有标签/分组(含 tagId 和名称)
- 按标签查听记:`minutes tag query --tag-id <tagId> [--limit 20] [--cursor <token>]`
- tagId 来自 `tag list` 返回值,不可编造
- 支持分页,`--cursor` 传入上一次返回的 nextToken
**典型链路**:用户说"帮我看看'周会'标签下的听记" →
1. `dws minutes tag list --format json` → 按名称匹配找到 tagId
2. `dws minutes tag query --tag-id <tagId> --format json`
### minutes-permission(权限管理)
- 添加成员:`minutes permission add --ids <uuid1,uuid2> --member-uids <uid1,uid2> --policy 4`
- 需先通过 `aisearch person --query "<姓名>" --dimension name` 获取目标 userId
- policy0=不可见 / 1=仅查看 / 2=查看+下载 / 3=查看+下载+编辑 / 4=全部权限
- 移除成员:`minutes permission remove --ids <uuid1,uuid2> --member-uids <uid1,uid2>`
### minutes-upload(音频上传)
```bash
# 创建上传会话
dws minutes upload create --file-name "meeting.mp3" --file-size 61565431 --format json
# 上传完成后确认
dws minutes upload complete --session-id <sid> --format json
# 取消上传
dws minutes upload cancel --session-id <sid> --format json
```
### 最佳实践案例速查(详见 `dingtalk-minutes`
| 案例 | 场景 | 正确链路 |
|------|------|----------|
| 案例 1 | 听记 URL + 创建思维导图 | 提取 taskUuid → `mind-graph create``mind-graph status` 轮询;**禁止**走 app-development 或前端库 |
| 案例 2 | 替换文字后未引导热词 | 检查特殊字符 → `replace-text` → 追问加热词 `hot-word add` |
| 案例 3 | 查听记拉了不必要的转写 | 用户只要列表 → `list` 即可,**不要**自动拉 `get transcription` |
| 案例 4 | 拉完转写只输出时间线原文 | 拉完后追问按发言人聚类 → 引导匹配 → 调用 `speaker replace` 写回 |
| 案例 5 | 查某人说了什么不引导替换 | 推断发言人 → **用户确认** → 结构化总结 → 引导 `speaker replace` |
| 案例 6 | 通讯录+部门+转写三路印证 | Step 3 画像 + Step 4 `aisearch person` 并发 → 置信度 ≥70% → 确认 → 替换 |
| 案例 7 | grep 花名误判未参会 | **禁止**在转写文本里 grep 人名判参会;**必须**调 `aisearch person` |
| 案例 8 | 听记类 query 不走 dws | **禁止**用 session_search/browser_use/activity:search 替代 dws;模糊请求先 `list mine` |
| 案例 9 | 按标签筛选听记 | `tag list` → 按名称匹配 tagId → `tag query --tag-id <tagId>`**禁止**编造 tagId |
### 听记取数深度约束(0609 点踩 case 提炼)
> 详细说明见 `dingtalk-minutes`。
- **转写原文硬约束**:用户诉求含「聚焦原话/逐字/沟通细节/具体讨论了什么」等词时,**必须先调 `get transcription` 翻页拉全**,禁止仅凭 summary 出稿
- **数据源下钻**:听记维度**必须 `get summary`(或 `get transcription`)读正文**,严禁只取标题列表;scope 用 `all`;空时换窗重试或标注
- **听记链接解析**:聊天消息中遇到听记链接(`flash_minutes_detail`/`SHANJI`)→ 解析 `minutesId` → 调 `minutes get summary/transcription`,禁止把链接降级为关键词
- **忠实性约束**:源数据无某要素(行动项/责任人/数字)时禁止生成;统计字段基于实际取数计数,不得编造
- **多源全覆盖**:用户枚举多数据源时每个来源都必须调对应工具;瞬时错误重试;如实声明缺失来源,禁编无来源数字
### 间接意图识别铁律
query 未提"听记"但任务产出依赖会议讨论内容时(报告/总结/日报/复盘/商业分析/市场感知),听记采集是**必跑前置步骤**:
1. **铁律 A**:任务含"会议/讨论/沟通"信息需求 → `dws minutes list` 必跑
2. **铁律 B**:用户说"文档啥也没有" → 听记优先级更高(唯一结构化数据源)
3. **铁律 C**:多源聚合场景 → 每个被提及的数据源都必须有采集动作,听记侧 0 调用 = 严重失败
## #8 通讯录
### get-contact-self
`contact user get-self` → 当前用户 userId、部门、主管等
### search-person
**搜人首选入口**。凡是“找人/搜人/找同事/谁负责/上级/下级/负责人/团队成员”均优先用 `aisearch person`
1. 从用户问题中提取 keyword(人名/业务关键词)和 dimension(维度),规则见 `dingtalk-aisearch`
2. `aisearch person --query "<关键词>" --dimension <维度>`
3. 结果中提取 `userId``title`(姓名)展示给用户。
4. 若需要 userId 做后续操作(发消息/建待办),可直接使用结果中的 `userId`
5. **重名消歧**:多人同名时禁止默认选第一个,须追加 `contact user get --ids` 获取部门/职位后请用户确认,详见 `dingtalk-contact/references/08-directory.md`「多命中」。
### search-user
仅在以下**精确查询**场景使用,搜人请优先用 `search-person`
- 需要获取 userId 给其他产品使用(发消息/建待办/约日程)
- 已有 userId 需查完整详情(`contact user get --ids`
1. `aisearch person --query "<姓名>" --dimension name``userId`;**多命中须列出候选请用户确认**。
2. **重名消歧**:多人同名时禁止默认选第一个,须追加 `contact user get --ids` 获取部门/职位后请用户确认,详见 `dingtalk-contact/references/08-directory.md`「多命中」。
3. 需详情时:`contact user get --ids <userId>`(多人可 `--ids id1,id2,...`
## #9 邮件
### mail-list-mailbox
查询当前用户自己的可用邮箱地址列表。**仅返回自己的邮箱**,不能查他人邮箱(查他人邮箱请走 `dingtalk-mail` 中「查找他人邮箱地址」三路并发查询流程)。
`mail mailbox list`
### mail-search
搜索邮件。**必须使用 KQL 语法通过 `--query` 传递查询条件**,禁止臆造 `--subject``--from` 等不存在的 flag。
1. 获取邮箱地址:`mail mailbox list` → 取用户邮箱地址。若用户已提供邮箱可跳过。
2. 构造 KQL 查询:根据用户意图将搜索条件转为 KQL 表达式(详见 `dingtalk-mail` 中 KQL 查询字段说明)。
- 按主题:`subject:周报``subject:"项目 进展"`(含空格须加双引号)
- 按发件人:`from:alice@company.com``from:"张三"`
- 按日期:`date>2025-06-01T00:00:00Z`(ISO8601 格式,必须含时间部分)
- 按文件夹:`folderId:2`(2=收件箱, 1=已发送, 5=草稿, 6=已删除)
- 按是否有附件:`hasAttachments:true`
- 组合:`from:alice AND subject:周报 AND date>2025-06-01T00:00:00Z`
3. 执行搜索:`mail message search --email <邮箱> --query "<KQL表达式>" --limit 20`
4. 查看详情(按需):`mail message get --email <邮箱> --id <messageId>`
### mail-send
发送邮件。
1. 获取邮箱地址:`mail mailbox list` → 取用户邮箱作为 `--from`
2. 确定收件人:用户直接提供邮箱地址 → 直接使用;用户提供姓名 → 走「查找他人邮箱地址」三路并发流程(见 `dingtalk-mail`)。
3. 发送:`mail message send --from <发件邮箱> --to <收件邮箱> --subject "<主题>" --content "<正文>"`(可选 `--cc``--attachment``--inline-attachment`)。
### mail-reply-forward
回复或转发邮件。
1. 获取邮箱地址:`mail mailbox list` → 取用户邮箱。
2. 定位原始邮件:若用户未提供 messageId → 先用 `mail-search` 搜索定位。
3. 执行:
- 回复:`mail message reply --from <邮箱> --id <messageId>`(可选 `--to``--subject``--content`
- 回复全部:`mail message reply-all --from <邮箱> --id <messageId>`(可选 `--to``--subject``--content`
- 转发:`mail message forward --from <邮箱> --to <收件邮箱> --id <messageId>`(可选 `--subject``--content`
@@ -0,0 +1,67 @@
# Recipe 规范
> 每个 recipe 是一个 **SKILL.md**,格式与产品 skill 同等身份。
## 架构三层
```
Layer 1: _common/conventions.md → 全局共享(认证、安全规则、字段术语)
相当于 gws-shared/SKILL.md
Layer 2: products/*.md → 产品能力(命令用法、flags、示例)
相当于 gws-gmail/SKILL.md, gws-docs/SKILL.md
Layer 3: recipe-xxx/SKILL.md → 组合配方(固定步骤 + 声明依赖)
相当于 recipe-send-team-announcement/SKILL.md
```
## Recipe SKILL.md 格式
```yaml
---
name: recipe-<verb>-<object>
description: "一句话描述"
metadata:
category: "recipe"
domain: "<领域编号>"
requires:
bins:
- dws
skills:
- <product-1>
- <product-2>
---
```
**正文**只包含:
1. 标题 + 一句话描述
2. `> **PREREQUISITE:** ...` 提示加载哪些 skill
3. `## Steps` — 编号步骤,每步一条 `dws` 命令
**不包含**(这些留给 conventions.md 和 best_practice.md):
- 安全门控/决策分支/踩坑提醒
- 失败处理策略
- 数据佐证
## 命名规则
```
recipe-<verb>-<object>[-<modifier>]
```
| 动词 | 含义 | 示例 |
|------|------|------|
| send | 发送 | recipe-send-message |
| create | 创建 | recipe-create-todo |
| query | 查询 | recipe-query-doc |
| write | 写入 | recipe-write-doc |
| resolve | 解析 | recipe-resolve-contact |
| generate | 生成 | recipe-generate-daily-report |
| share | 分享 | recipe-share-doc-and-notify |
## 路由规则
Agent 识别意图后:
1. 匹配到 recipe → 加载 recipe 声明的 skills → 按 Steps 执行
2. 无匹配 recipe → 走领域 best_practice.md 策略指南
3. 只需单个命令 → 直接查 products/*.md
@@ -0,0 +1,52 @@
# 产品路由
仅在用户泛称 DWS/钉钉操作,或者无法从意图直接选择单一产品 skill 时读取。调度器
已经命中清晰产品 skill 时不要读取本文件。
## 一级产品选择
| 用户目标 | 读取目标 |
|---|---|
| AI 表格、多维表、字段、记录、视图、仪表盘 | [`dingtalk-aitable`](../../dingtalk-aitable/SKILL.md) |
| 日程、参会人、会议室、闲忙 | [`dingtalk-calendar`](../../dingtalk-calendar/SKILL.md) |
| 群聊、消息、机器人、Webhook、群成员 | [`dingtalk-chat`](../../dingtalk-chat/SKILL.md) |
| 实时监听未来 IM 消息、reaction、已读、撤回、群生命周期或 OA 审批变化 | [`dingtalk-event`](../../dingtalk-event/SKILL.md) |
| 已有 userId 的用户详情、部门、角色、组织关系 | [`dingtalk-contact`](../../dingtalk-contact/SKILL.md) |
| 文档正文读取、创建、更新、块编辑、媒体和导出 | [`dingtalk-doc`](../../dingtalk-doc/SKILL.md) |
| 文件搜索、上传下载、复制移动、重命名、权限 | [`dingtalk-drive`](../../dingtalk-drive/SKILL.md) |
| 邮件查询、搜索、读取和发送 | [`dingtalk-mail`](../../dingtalk-mail/SKILL.md) |
| 听记列表、摘要、转写、关键字和标题 | [`dingtalk-minutes`](../../dingtalk-minutes/SKILL.md) |
| 待办创建、查询、更新、完成和删除 | [`dingtalk-todo`](../../dingtalk-todo/SKILL.md) |
| 知识库/钉盘空间、空间节点和成员管理 | [`dingtalk-wiki`](../../dingtalk-wiki/SKILL.md) |
| 姓名模糊找人、负责人、上下级、工号、手机号语义线索、企业知识和行为记录搜索 | [`dingtalk-aisearch`](../../dingtalk-aisearch/SKILL.md) |
| 完整手机号精确反查,或已有 userId 后查人员详情、部门和角色 | [`dingtalk-contact`](../../dingtalk-contact/SKILL.md) |
| Markdown / `.md` 内容读取、创建、覆盖、局部修改或版本差异比较 | [`dingtalk-misc`](../../dingtalk-misc/SKILL.md) → [`markdown.md`](../../dingtalk-misc/references/markdown.md) |
| 组织大脑、人才池、员工档案专项、职业历程、绩效、结构化人才搜索 | [`dingtalk-misc`](../../dingtalk-misc/SKILL.md) → [`hrbrain.md`](../../dingtalk-misc/references/hrbrain.md) |
| PAT 行为授权、scope 授权、授权浏览器策略 | [`dingtalk-misc`](../../dingtalk-misc/SKILL.md) → [`pat.md`](../../dingtalk-misc/references/pat.md) |
| 切换组织、跨组织、多组织、profile 管理 | [`dingtalk-misc`](../../dingtalk-misc/SKILL.md) → [`profile.md`](../../dingtalk-misc/references/profile.md) |
| 审批查询与处理、考勤、会议、电子表格、日志、DING、直播、开放平台应用、技能市场安装等长尾产品 | [`dingtalk-misc`](../../dingtalk-misc/SKILL.md) |
| 宜搭 / AI 应用创建脚本 / 财务辅助脚本(无稳定产品面) | [`dingtalk-misc`](../../dingtalk-misc/SKILL.md) → [`unsupported-scripts.md`](../../dingtalk-misc/references/unsupported-scripts.md);默认说明未产品化,勿当正式 CLI |
选择 `dingtalk-misc` 后,先读取其 `SKILL.md` 产品索引,再只读取命中产品的单个
reference,不要加载全部长尾产品文档。
## 高频边界
- `aisearch person`:按姓名、职责、上下级、工号或手机号线索语义找人;`contact`
完整手机号精确反查,或拿到 userId 后查详情、部门和角色;`mail`:邮件内容与收发。
- `drive`:对任何文件都成立的存储操作;`doc`:文档正文和块内容;`wiki`:空间与
节点组织。
- `.md` 内容读写走 `markdown``dingtalk-misc`);复制、移动、删除等实体操作走 `drive`
- `aitable`:字段/记录式数据表;`sheet`:单元格、公式、多工作表。`sheet` 位于
[`dingtalk-misc`](../../dingtalk-misc/references/sheet.md)。
- `calendar`:日历事件、参会人和会议室;视频会议(conference)当前 CLI 不支持,请在钉钉客户端操作;
`minutes`:会后听记内容。
- `report`:钉钉日志系统中的日报/周报;`doc`:普通文档创作;`todo`:个人任务。
- `chat`:发送消息、读取历史消息和主动群操作;独立的 `event`:未来个人 IM/OA 事件长连接监听;
`ding`:强提醒,位于 `dingtalk-misc`
- `hrbrain` / `markdown` / `pat` / `profile` 均位于 `dingtalk-misc`
- 请假、加班、外出、出差、补卡等考勤业务审批优先走 `attendance`;其他通用审批查询、同意、
拒绝、转交和撤销走 `oa`。两者均位于 `dingtalk-misc`;未来审批任务或实例变化的实时通知走
独立的 `dingtalk-event`
边界仍无法判断时,只读取 [intent-guide.md](intent-guide.md) 的对应章节。
@@ -0,0 +1,11 @@
## 最小 DWS 执行契约
- 只通过 `dws` CLI 操作钉钉;结构化读取使用 `--format json`,按真实返回判断结果。
- 已知命令直接执行。只有 leaf 参数或安全语义不确定时读取精确 Schema,只有 Cobra flag 不确定时读取精确 leaf Help;不要加载产品级 Catalog 代替选路。
- 不猜命令、flag、字段、ID、账号或时间。后续 ID 必须来自真实返回;零命中、多候选或类型不明时停止并消歧。
- 解析目标、读取上下文和最终执行必须使用同一 profile;不得跨组织复用 userId、openDingTalkId 或 openConversationId。多账号组织只使用明确的 `isOrgCurrent=true` 默认账号;没有默认账号时要求用户指定,禁止选择第一项、最近登录或最近使用账号。
- 不输出或记录 token、refresh token、appSecret、webhook token 等凭据;宿主已注入认证时不要索要凭据。
- 写操作必须符合用户明确意图。是否需要确认以最终 Runtime gate 和 Schema 为准;需要确认时先说明对象、动作与影响,再追加 `--yes`
- 写后按任务结果契约验证;不能仅凭退出码宣称成功。部分结果、未知投递状态和失败项必须如实保留。
- 时间戳面向用户展示时转换为带时区的可读时间;默认使用当前会话时区,必要时同时保留原值。
- 遇到认证、权限、profile、confirmation 或未知错误时,只加载 `dingtalk-shared` 中对应 reference;不要连续猜测替代命令。
@@ -0,0 +1,88 @@
# Schema 渐进查询
`dws schema` 内嵌当前二进制公开命令面的结构化契约。**Agent 选择命令、读取参数映射/约束和安全语义时优先渐进查询 leaf Schema**;真正组装执行参数前,用 `--help` 确认当前 Cobra 接受的 flags。
本节同时适用于基础/原子命令与公开内建 `+` shortcut。用户自定义或未公开 shortcut 不进入发布 Schema;是否可执行仍以当前 Cobra help 为准。
## 已知命令路径例外
当产品 Skill、意图表或任务 reference **已经给出精确 CLI path** 时:
- 不要再查产品级/分组级 Schema,也不要加载完整 Shortcut Catalog
- 可直接执行;只有参数、约束或安全语义不确定时才读该命令的 leaf Schema
- 只有当前 Cobra flags 不确定时才补读 leaf Help
稳定 command identity 已与真实 Cobra tree 绑定。不要读取 Catalog 文件、native annotation 或其他生成 JSON 来重新推断命令。
## 四层查询
```bash
# 第 1 层:产品概览(列出产品 + 工具数 + 用途摘要)
dws schema
# 第 2 层:产品级(该产品工具的 cli_path + description + effect/risk
dws schema calendar --compact
# 第 3 层:分组级
dws schema "calendar event" --compact
# 第 4 层:Agent leaf(参数契约)
dws schema "calendar event create" --compact
# --all:全量 leaf,仅 CI / 审计 / 参数 baseline
dws schema --all --format json
```
### `--all` 边界(强制)
`--all` 输出体积很大。仅在用户明确要求全量导出,或 CI / Catalog 审计 / 参数防丢 baseline 时使用。普通业务任务严禁用 `--all` 做命令发现,也不要把全量结果注入 Agent 上下文;必须按「产品概览 → 产品/分组 → leaf」渐进查询。
完整兼容性 baseline 必须使用未裁剪的 `schema --all``schema --all --compact` 会移除 provenance 和接口映射字段,不得作为完整 baseline。
同一工具省略 `--compact` 的 full leaf 与 `--all` 条目是同一份 `ToolSpec` 契约;compact leaf 只做展示投影,不重新解析语义。Alias 查询只改变路径视图,不得据此重写参数。若同一视图观察到内容差异,按契约漂移报告,不要选一份继续猜。
### `--compact`
正向字段白名单:保留 `cli_path``canonical_path``description``effect``risk``confirmation``interface_mode``availability``interface_reason``parameters``constraints``examples``use_when``avoid_when`;新增 full/audit 字段不会自动泄漏进 Agent 上下文。它有意不返回 `interface_ref`、参数 `property/interface_type` 和 provenance(如 `agent_metadata_source``effect_source``primary_cli_path` 等);检查这些映射事实时,用 full leaf 配合 `--jq` 精确投影。
若旧二进制报 `unknown_flag: --compact`,去掉 `--compact` 重跑同一查询;不要因此判定 leaf 不存在,也不要用 Schema 查业务数据。
## 字段速查
```jsonc
{
"cli_path": "calendar event create",
"effect": "write", // read | write | destructive
"risk": "medium", // low | medium | high
"confirmation": "not_required", // not_required | user_required
"availability": "available",
"parameters": { "title": { "type": "string", "required": true } },
"constraints": { "require_together": [["a", "b"]] }
}
```
- `confirmation=user_required` → 先确认再加 `--yes`;协议见 [confirmation.md](./confirmation.md)
- `availability=unavailable` → 不执行;说明 `interface_reason`
- `parameters.<flag>.required=true` / `cli_required=true` → 按 Schema/Cobra 契约提供参数
- `constraints.require_together` → 列出的 flag 必须同时提供
## Schema、Help 与业务数据边界
| 信息 | 事实源 |
|---|---|
| 命令是否存在、Cobra 接受哪些 flags | `dws <cli_path> --help` |
| Agent 选择、CLI 参数/required/组合约束、risk/confirmation | `dws schema "<cli_path>" --compact` |
| CLI↔RPC 参数映射、接口绑定或 provenance 审计 | full leaf 配合 `--jq` / `--fields` 精确投影;不要把整个 full leaf 注入 Agent 上下文 |
| shortcut 同上 | 已知路径:`dws schema --cli-path "<service> +<shortcut>" --compact --format json` |
| 钉钉业务数据 | 真实 `read` / `search` / `list` 等命令 |
Schema 与 Help 冲突是**契约漂移**:执行参数只用 Cobra 接受的 flags;安全语义冲突时采用更保守确认,无法确认则停止并报告。
`dws schema` 只查询命令契约。完成发现后必须继续执行真实业务命令;不要把 Schema 结果当成业务查询结果。
### 两类易混 Schema
- `dws event schema <event_key> --flatten`:事件业务字段
- `dws schema "event consume" --compact`CLI 命令参数
二者不可互相替代。
@@ -0,0 +1,144 @@
# URL 格式与处理规范
## 路由第 0 步:意图直达(优先级高于 URL 探测)
用户已经明确表达某产品的内容意图时,直接进入对应产品场域,不要先做 URL 类型
探测。尤其:
- 明确提到 Markdown / `.md` 文件的读取或修改,按普通文件走 `drive` 场域:
`dws drive download` 下载到本地处理,再用 `dws drive upload` 回传。
- 明确“读这篇文档 / 编辑文档正文”进入 `doc`;明确“看这个在线表格数据”进入
`sheet`
仅当用户只粘贴 URL、没有明确产品意图,或意图与链接类型可能冲突时,才执行下方
类型探测。
## alidocs URL 分流决策(意图不明确时执行)
收到 `alidocs.dingtalk.com` URL 且无法从指令判断产品时,必须按以下顺序判断:
1. URL 路径含 `/i/p/`**分享短链**,禁止调用 `dws doc` 任何子命令 → 按下方 [分享短链处理](#分享短链处理) 执行
2. URL 路径含 `/i/nodes/`**节点链接**,需探测类型 → 按下方 [alidocs URL 类型探测流程](#alidocs-url-类型探测流程) 执行
3. URL 路径含 `/spreadsheetv2/`**电子表格直链**,直接路由到 `sheet`,将完整 URL 原样传给 `--node` 参数
4. URL 路径含 `/document/edit``/document/preview` 且 query 参数包含 `dentryKey`**文档链接**,直接路由到 `doc`,将完整 URL 原样传给 `--node` 参数(URL 中不一定有 `type=d`,只需匹配路径和 `dentryKey` 参数即可)
5. 其他 alidocs URL 格式 → 告知用户当前暂不支持该链接格式
---
## 已知 URL 格式
需要自行拼接链接时,只能使用以下模板:
| 产品 | 用途 | URL 格式 | ID 来源 |
|------|------|----------|---------|
| `aitable` | AI表格 Base 链接 | `https://alidocs.dingtalk.com/i/nodes/{baseId}` | `base list/search/create/get` 返回的 `baseId` |
| `aitable` | AI表格指定数据表链接 | `https://alidocs.dingtalk.com/i/nodes/{baseId}?iframeQuery=sheetId%3D{tableId}` | `baseId` + `table create/get``base get` 返回的 `tableId` |
| `aitable` | AI表格指定数据表+视图链接 | `https://alidocs.dingtalk.com/i/nodes/{baseId}?iframeQuery=sheetId%3D{tableId}%26viewId%3D{viewId}` | `baseId` + `tableId` + `view create/get` 返回的 `viewId` |
| `aitable` | AI表格模板预览 | `https://docs.dingtalk.com/table/template/{templateId}` | `template search` 返回的 `templateId` |
| `doc` | 文档链接 | `https://alidocs.dingtalk.com/i/nodes/{dentryUuid}` | `doc` 命令返回的 `dentryUuid` |
| `sheet` | 电子表格链接 | `https://alidocs.dingtalk.com/i/nodes/{dentryUuid}` | `sheet create` 返回的 `dentryUuid` |
| `sheet` | 电子表格直链 | `https://alidocs.dingtalk.com/spreadsheetv2/{key}/...?dentryKey={key}&type=s` | 用户提供的完整 URL,直接传给 `--node` |
| `doc` | 文档链接(edit/preview | `https://alidocs.dingtalk.com/document/{edit\|preview}?...&dentryKey={key}` | 用户提供的完整 URL,直接传给 `--node` |
| `minutes` | 听记链接 | `https://shanji.dingtalk.com/app/transcribes/{taskUuid}` | `list mine/shared` 返回的 `taskUuid` |
不在此表中的产品,禁止自行拼接 URL。命令返回中包含完整链接时直接使用,否则告知用户无法提供。
## 分享短链处理
`alidocs.dingtalk.com/i/p/{shortKey}` 是钉钉文档的**对外分享短链**`dws doc` 命令无法解析此格式。
### 识别规则
URL 路径中包含 `/i/p/` 即为分享短链(无论后面是否还有子路径),例如:
- `https://alidocs.dingtalk.com/i/p/Y7kmbokZp3pgGLq2`
- `https://alidocs.dingtalk.com/i/p/Y7kmbokZp3pgGLq2/docs/AY39rGpMPmeVNpXZevZm8OZkXKnaoNQ7`
- `https://alidocs.dingtalk.com/i/p/AbCdEfGh1234`
- `https://alidocs.dingtalk.com/i/p/AbCdEfGh1234/sheets/XYZ789`
> **关键**:只要 URL 中出现 `/i/p/`,无论后面跟什么子路径(`/docs/...`、`/sheets/...` 等),都属于分享短链,一律禁止调用 `dws doc`。
### 处理方式
**不要调用 `dws doc` 任何子命令**(包括 `doc info``doc read` 等),`dws` 无法解析此格式。
- **需要获取文档内容时**:使用 `read_url` 工具直接读取该链接
- **其他操作(如移动、复制、权限管理等)**:告知用户此链接为分享短链,无法直接执行复制、移动、权限管理等操作。如需保存该文档内容,建议用户在钉钉客户端中打开该页面,手动复制文本内容,然后可通过 `dws doc create` 创建一篇新文档并将内容写入
```
# 需要读取文档内容时(无论 /i/p/ 后面有没有子路径,都用 read_url)
read_url("https://alidocs.dingtalk.com/i/p/Y7kmbokZp3pgGLq2")
read_url("https://alidocs.dingtalk.com/i/p/Y7kmbokZp3pgGLq2/docs/AY39rGpMPmeVNpXZevZm8OZkXKnaoNQ7")
# 禁止(以下全部会失败,dws 无法解析任何含 /i/p/ 的 URL)
dws doc info --node "https://alidocs.dingtalk.com/i/p/Y7kmbokZp3pgGLq2" --format json
dws doc read --node "https://alidocs.dingtalk.com/i/p/Y7kmbokZp3pgGLq2/docs/AY39rGpMPmeVNpXZevZm8OZkXKnaoNQ7" --format json
```
### 当 `read_url` 返回内容不完整时
钉钉文档分享页是动态渲染的,`read_url` 可能只能获取到页面标题等有限信息,无法获取文档正文。此时**禁止猜测原因**(如"权限不足""文档为空""文档已删除"等),**禁止建议用户"提供 `/i/nodes/` 格式链接"**(分享短链和节点链接是不同体系,普通用户无法自行转换)。应直接告知用户:
> 这个链接是钉钉文档的分享短链,由于页面是动态渲染的,我无法通过该链接直接获取文档的完整正文内容。
>
> 你可以:
> 1. 在钉钉客户端中打开该文档,将正文内容复制粘贴给我
> 2. 如果文档已保存在你的文档空间中,可以告诉我文档名称,我通过 `dws drive search` 搜索后再读取
---
## alidocs URL 类型探测流程
`alidocs.dingtalk.com/i/nodes/{id}` 是钉钉文档空间的统一 URL,可能指向**文档、电子表格、多维表、文件、文件夹**等不同类型。**禁止仅凭 URL 就假定为文档**,必须先探测类型再路由到正确的产品。
### 探测步骤
```
Step 1 → dws drive info --node "<URL>" --format json
Step 2 → 从返回中提取 extension、nodeType 字段
Step 3 → 按下方路由规则映射到对应产品
```
> 路由依据是 `extension`,不是 `contentType`。`drive info` 检测到
> `adoc` / `axls` / `able` 时会自动补充在线文档信息。
### 路由映射表
| 条件 | 路由到产品 | 后续操作 |
|------|-----------|---------|
| `extension=adoc` | `doc` | 加载 `dingtalk-doc` 操作内容 |
| `extension=axls` | `sheet` | 加载 `dingtalk-misc``references/sheet.md` 操作(仅 `axls` 在线电子表格) |
| `extension=able` | `aitable` | 将 nodeId 作为 baseId,加载 `dingtalk-aitable` 操作 |
| `extension=xlsx` / `xls` / `xlsm` / `csv` | `drive` | 必须用 `dws drive download` 下载到本地处理,禁止走 `sheet` |
| `nodeType=file`(非在线文档扩展名,含 `md` | `drive` | 下载用 `dws drive download --node <ID> --output <PATH> --format json`;上传/覆盖用 `dws drive upload` |
| `nodeType=folder` | `drive` / `wiki` | 调用 `dws drive list --workspace <WS_ID>``dws wiki node list` 列出子节点 |
| 以上均不匹配 | — | 告知用户当前暂不支持该类型 |
> axls vs xlsx 关键区分:
> - `axls`(钉钉在线电子表格,`contentType=ALIDOC`)→ 走 `sheet` 产品线(读/写/筛选/导出等服务端原子操作)
> - `xlsx` / `xls` / `xlsm` / `csv`(上传到文档空间的本地表格文件,`contentType=DOCUMENT`)→ 必须走 `dws drive download` 下载到本地后再解析处理,严禁错误路由到 `sheet` 产品线(sheet 命令只支持在线表格,调用 xlsx 节点会直接报错)
> - 用户想把在线表格导出为 xlsx 文件 → 用 `dws sheet export`(输入是 `axls`,输出是 xlsx,这是 axls → xlsx 的格式转换,不属于 xlsx 读取场景)
### 示例
```bash
# 用户传入: https://alidocs.dingtalk.com/i/nodes/abc123
dws drive info --node "https://alidocs.dingtalk.com/i/nodes/abc123" --format json
# 返回 extension=axls → 在线电子表格,路由到 sheet
dws sheet list --node "https://alidocs.dingtalk.com/i/nodes/abc123" --format json
# 返回 extension=xlsx/xls/csv → 本地表格文件,必须下载处理(禁止走 sheet)
dws drive download --node "https://alidocs.dingtalk.com/i/nodes/xlsx456" --output <PATH> --format json
# 返回 nodeType=file → 普通文件,下载
dws drive download --node "https://alidocs.dingtalk.com/i/nodes/def456" --output <PATH> --format json
# 返回 nodeType=folder → 文件夹,列出子节点
dws drive list --workspace <WS_ID> --format json
```
### 何时可跳过探测
当用户指令中已明确指定产品(如"帮我读这个文档"、"看下这个表格的数据"),可结合用户意图**跳过探测**直接路由。仅在以下情况**必须执行探测**:
- 用户只粘贴 URL,无其他上下文
- 用户指令与 URL 实际类型可能不一致(如说"文档"但实际是表格)
@@ -0,0 +1,45 @@
# 跨产品编排路由
仅在请求包含多个时序步骤、跨产品数据传递、批量采集、汇总分析或报告交付时读取。
当前发布包不包含独立 scenario skill;跨产品请求由本文件选择行动指南,再组合产品
skill 完成。
## 选择顺序
1. 按下表选择与用户目标最匹配的产品行动指南。
2. 显式读取流程涉及的产品 `SKILL.md`,不要预加载无关产品。
3. 前一步返回的真实 ID、URL 或结构化数据作为后一步输入。
4. 只读采集可并行;依赖上一步输出的步骤、写操作和验证步骤必须保持顺序。
## 产品行动指南
| 场景 | 典型目标 | 读取目标 |
|---|---|---|
| 消息沟通 | 发消息、查聊天、建群、机器人/Webhook 推送 | [`01-messaging.md`](../../dingtalk-chat/references/01-messaging.md) |
| 任务管理 | 创建/查询/完成待办、从来源提取任务 | [`02-task.md`](../../dingtalk-todo/references/02-task.md) |
| 会议日程 | 日程、会议室、闲忙、改期 | [`03-meeting.md`](../../dingtalk-calendar/references/03-meeting.md) |
| 文档知识 | 搜索、创建、编辑、分享文档和知识库分流 | [`04-document.md`](../../dingtalk-doc/references/04-document.md) |
| 工作汇报 | 日报周报、日志、多源会议或项目报告 | [`05-reporting.md`](../../dingtalk-misc/references/05-reporting.md) |
| 数据分析 | AI 表格读写、模板建表、统计报告 | [`06-data-analytics.md`](../../dingtalk-aitable/references/06-data-analytics.md) |
| 听记与会后 | 摘要、转写、会后待办和通知 | [`07-minutes.md`](../../dingtalk-minutes/references/07-minutes.md) |
| 通讯录 | 找人、部门、上下级、负责人和精确用户信息 | [`08-directory.md`](../../dingtalk-contact/references/08-directory.md) |
## Lite 与 Full
- 单一目标、短链路、无多源汇总:在
[lite-catalog.md](recipes/lite-catalog.md) 中只读取对应 recipe。
- 涉及多源采集、批量处理、交叉分析、汇总/归纳或三个以上产品:使用上表的完整
行动指南,并按 [conventions.md](recipes/conventions.md) 执行批量和
ID 传递规则。
- 产品 skill 已内联完整步骤时,直接执行其步骤,不再重复读取通用 recipe。
## 多场景消歧
- “催审批”是 OA 审批操作,走 `dingtalk-misc`,不是普通消息发送。
- “会后/听记生成待办”优先听记场景,不是普通待办创建。
- “安排日程/订会议室”走 calendar;“发起或预约视频会议”当前 CLI 不支持,请在钉钉客户端操作;“读取
会后内容”走 minutes。
- “汇总多次会议/多个来源并生成报告”走工作汇报行动指南,不是单篇文档编辑。
- “整理项目全部讨论”是跨源汇总;“编辑指定文档”才是单产品 doc 操作。
仍有歧义时,读取 [intent-guide.md](intent-guide.md) 的相关章节,不要全文加载。