SKILL DETAIL
meegle
larksuite/meegle-cli/meegle
This skill operates Feishu project data via the Meegle CLI, covering work items, workflows, views, todos, schedules, comments, deliverables, resource libraries, WBS plans, and more. It supports querying work items with MQL, creating/updating/batch-getting work items, completing node or state transitions, listing work items under a view, querying personal todo/done/overdue/this-week items, retrieving user schedules and workload details, adding comments, managing deliverables, creating resource instances, and editing/publishing WBS drafts. Before use, complete the authorization flow and refer to the CLI guide for command structure. Detailed parameters and examples for each command are in the corresponding reference documents. Output language follows the user's input language, defaulting to Chinese.
Installation
npx skills add https://github.com/larksuite/meegle-cli --skill meegle
スキルファイル
SKILL.md
最終同期 · 2026/08/28
references/ai-handoff.md›
# AI 助手续处理细则
本文件用于在 Meegle CLI 已完成可执行部分,或明确无法执行用户请求后,判断是否提供 AI 助手兜底链接。链接是补充入口,不替代 CLI 结果。
## 适用范围
| 场景 | 示例 | CLI 先做什么 | 续处理目标 |
| --- | --- | --- | --- |
| 分析、追溯、总结、诊断、研判 | “这些需求为什么卡住了” | 查询状态分布和操作记录 | 分析共性原因、风险与建议 |
| 排期或工时分析 | “这周我们组是不是超载了” | 能查询则先返回排期明细 | 分析负载与冲突 |
| 历史变更或延期追溯 | “这个需求为什么拖这么久” | 查询操作记录 | 总结关键变更与延期原因 |
| 周报或待办总结 | “帮我写个周报” | 查询待办和必要的操作记录 | 生成结构化周报 |
| CLI 不支持的工作项操作 | “把这几个需求冻结” | 明确 CLI 当前不能执行 | 让 AI 助手继续完成操作 |
| 度量图表生成或修改 | “这张图加个维度” | 能查询则先返回现状 | 让 AI 助手继续修改图表 |
以下情况不提供链接:纯数据查询、导出或列举且没有分析意图;单实例详情查看;用户明确只要数据;用户已拒绝;本会话已主动引导过一次。
## 处理流程
1. **先完成 CLI 范围内的工作**:有可执行的查询或操作时,先返回真实结果。请求完全不受 CLI 支持时,说明能力边界后继续判断,不要为了满足流程而伪造查询。
2. **检查可用性**:调用 `ai-handoff availability`。`available=false` 时停止;如果原请求只能续处理,可简短说明入口当前不可用。成功结果按 profile 缓存约一小时,同一会话不重复预检;最终创建仍由服务端重新校验。
3. **遵守模式**:
- `auto`:符合适用范围时继续生成链接;
- `ask`:先问用户是否要在 AI 助手中继续。得到明确同意前不得调用 `ai-handoff create-link`;同意后可直接创建,无需重复预检;
- `off`:停止,不提示用户修改偏好。
4. **构造 query**:只描述 AI 助手要继续完成的增量任务,遵循下方规则。
5. **构造关联上下文**:只传恢复业务对象所需的 ID 指针,遵循下方类型和降级策略。
6. **创建链接**:调用 `ai-handoff create-link`。只有返回 `available=true` 且包含 `url` 时才附链接;返回 `available=false` 时按停止信号处理。不要硬编码域名,CLI 会按当前登录环境归一化 host。
7. **附在回复末尾**:说明链接能继续完成什么,例如:`如需继续分析卡点原因和处理建议,可在 AI 助手中继续:<url>`。不得暗示 CLI 查询结果集已完整传入。
用户拒绝后,本会话不再提示。用户明确要求持久关闭或重新开启时,分别调用 `preference handoff off` 或 `preference handoff auto`;只有用户明确要求“先询问”模式时才调用 `preference handoff ask`。
## query 构造规则
query 写成用户可直接理解的自然语言,不暴露 CLI、MQL、命令或内部编排过程。自然包含:
- 续处理目标;
- 对象范围和筛选语义,如空间、工作项类型、状态、负责人、时间范围;
- 已观察到的现象或当前状态摘要;
- 希望 AI 助手继续完成的分析、建议或操作。
关联上下文只提供 ID 指针,不包含 CLI 完整结果。上下文粒度不足时,query 必须补充“范围指纹”:对象类型、关键状态、负责人、时间范围与共性特征。不要写入完整对象清单,也不要把分页结果数当成真实总数。
示例:
```text
分析 Meego 空间下状态为处理中且本周有更新的需求,重点判断哪些进度滞后、卡在哪个环节、有哪些风险,并给出优先关注对象和处理建议。
```
## 关联上下文策略
按以下优先级选择 payload。当前续处理策略不使用 type=2(WorkItemType)。
| 优先级 | type | 名称 | payload | 适用场景 |
| ---: | ---: | --- | --- | --- |
| 1 | 4 | View | `view.project_key`、`view.view_id`、可选 `view.work_item_type_key` | 用户提供视图链接,范围还原最稳定 |
| 2 | 3 | WorkItem | `work_item.project_key`、`work_item.work_item_type_key`、`work_item.work_item_id` | 少量明确工作项,默认不超过 3 条 |
| 3 | 1 | Project | `project.project_key` | 批量结果、跨类型或无法传更细粒度对象 |
| — | 5 | MeasureChart | `measure_chart.project_key`、`measure_chart.chart_id` | 度量图表场景 |
- 用户提供视图或工作项 URL 时,先调用 `url decode`,从结果提取 ID;不要手工拆 URL。即使后续查询因权限失败,仍可用已解析的 ID 表达用户意图。
- type=3 只用于用户直接给出 URL 或明确锚点的少量对象。超过 3 条时退化为 type=1,并在 query 中补足范围指纹。接口返回的 `limits.max_related_context_items` 只是协议上限,不是建议逐条传满。
- `--related-context` 接受严格 JSON 对象或数组。出现解析错误时,只修正一次 JSON 引号、字段名或 type/payload 对应关系;这类本地参数修正不计为链接创建重试。
工作项示例:
```bash
meegle ai-handoff create-link --query '分析这个工作项的延期原因并给出下一步建议' --related-context '{"type":3,"work_item":{"project_key":"space_key","work_item_type_key":"story","work_item_id":"123456"}}' --format json
```
## 失败与重试
- `available=false`、`mode=off`、`HANDOFF_REJECTED` 或 `LOCAL_DISABLED` 都是明确停止信号,不重试、不建议用户绕过策略。
- `ai-handoff create-link` 已内置传输层重试。任意创建失败后都不要从 Skill 再次调用,避免重复生成链接;向用户简要说明链接暂不可用即可。
- 每轮最多生成一条链接,同一会话最多主动引导一次。用户随后明确要求重新生成时,才把它视为新的显式请求。
references/api-examples.md›
# 命令调用示例
> 参数占位符(`{{xxx}}`)表示按需填入;未使用的可选参数可省略。所有命令统一加 `--format json` 以获得结构化输出。
---
## 空间域
### project search
按名称或 key 搜索:
```bash
meegle project search --project-key 空间名或key --page-num 1 --format json
```
省略 project_key 可列出当前用户可访问的空间(按最近访问排序)。
---
## 工作项域
### workitem meta-types
```bash
meegle workitem meta-types --project-key 空间key --format json
```
### workitem meta-fields
```bash
meegle workitem meta-fields --page-num 1 --project-key 空间key --work-item-type story --field-types '{{field_types}}' --field-keys '{{field_keys}}' --field-query '{{field_query}}' --format json
```
### workitem meta-roles
```bash
meegle workitem meta-roles --page-num 1 --project-key 空间key --work-item-type story --role-keys '{{role_keys}}' --role-query '{{role_query}}' --format json
```
> **MQL 查询前必须先执行**:字段/枚举/树状字段用 `workitem meta-fields` 查 key 与 options;涉及角色并行调用 `workitem meta-roles` 取 `role_id` / `role_name`。字段与角色配置由「空间 + 工作项类型」双维度决定。业务线等树状字段直接传中文时必须传叶子节点名,禁拼完整路径。
### workitem query
首查:
```bash
meegle workitem query --project-key 空间key --mql 'SELECT `work_item_id`, `name`, `work_item_status` FROM `空间key`.`story` WHERE `archiving_date` IS NULL' --format json
```
无分组翻页时 group_id 传 `"1"`:
```bash
meegle workitem query --project-key 空间key --session-id 首查返回的session_id --mql '' --group-pagination-list '[{"group_id":"1","page_num":2}]' --format json
```
有分组翻页时,group_id 从首查 `list[].group_infos[].group_id` 取:
```bash
meegle workitem query --project-key 空间key --session-id 首查返回的session_id --mql '' --group-pagination-list '[{"group_id":"分组ID","page_num":3}]' --format json
```
### workitem get
基础查询:
```bash
meegle workitem get --work-item-id 工作项ID --fields '{{fields}}' --project-key 空间key --format json
```
只取拉群方式:
```bash
meegle workitem get --work-item-id 工作项ID --fields '["group_type"]' --project-key 空间key --format json
```
全量字段分页时 fields 传 `["_all"]`。Meegle CLI 的 page_size / page_token 必须通过 `--params` 传,避免被序列化为字符串:
```bash
meegle workitem get --work-item-id 工作项ID --fields '["_all"]' --project-key 空间key --params '{"page_size":100}' --format json
```
```bash
meegle workitem get --work-item-id 工作项ID --fields '["_all"]' --project-key 空间key --params '{"page_size":100,"page_token":"<next_page_token>"}' --format json
```
### workitem create
基础创建(仅标量):
```bash
meegle workitem create --work-item-type story --fields '[{"field_key":"template","field_value":"模板ID"},{"field_key":"name","field_value":"需求标题"}]' --project-key 空间key --format json
```
创建并指定报告人/经办人(role_owners 是 stringified JSON):
```bash
meegle workitem create --work-item-type issue --fields '[{"field_key":"name","field_value":"示例缺陷"},{"field_key":"template","field_value":"模板ID"},{"field_key":"role_owners","field_value":"[{\"role\":\"reporter\",\"owners\":[\"userkey1\"]},{\"role\":\"operator\",\"owners\":[\"userkey2\"]}]"}]' --project-key 空间key --format json
```
> **`role_owners` 中 `role` 字段填 `role_id`**(`workitem meta-roles` 返回的英文 key,如 `operator` / `reporter` / `role_xxxxxx`),不是 `role_name`。系统默认 `role_id`:报告人 `reporter`、经办人 `operator`。自定义角色的 `role_id` 形式多样,**必须先调 `workitem meta-roles` 确认**。创建时可在 `fields` 传 `role_owners`;更新已有工作项角色**必须**用 `workitem update` 的 `role_operate`。
### workitem update
更新普通字段:
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"priority","field_value":"option_id"}]' --format json
```
更新 multi-user(复合值 stringified):
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"current_status_operator","field_value":"[\"userkey1\",\"userkey2\"]"}]' --format json
```
普通复合字段分别使用 `add` / `update` / `delete` action:
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"复合字段key","field_value":"{\"action\":\"add\",\"fields\":[[{\"field_key\":\"子字段key\",\"field_value\":\"示例值\"}]]}"}]' --format json
```
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"复合字段key","field_value":"{\"action\":\"update\",\"group_uuid\":\"读回的组标识\",\"fields\":[[{\"field_key\":\"子字段key\",\"field_value\":\"新值\"}]]}"}]' --format json
```
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"复合字段key","field_value":"{\"action\":\"delete\",\"group_uuid\":\"读回的组标识\"}"}]' --format json
```
多人复合字段必须整体覆盖且不可用于新增人员:
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"多人复合字段key","field_value":"{\"userkey1\":[{\"field_key\":\"子字段key\",\"field_value\":\"示例值\"}],\"userkey2\":[]}"}]' --format json
```
更新拉群方式(group_type 逻辑字段):
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"group_type","field_value":"{\"type\":\"auto\"}"}]' --format json
```
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"group_type","field_value":"{\"type\":\"bind\",\"group_id\":\"oc_xxx\"}"}]' --format json
```
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --fields '[{"field_key":"group_type","field_value":"{\"type\":\"disabled\"}"}]' --format json
```
`auto` / `disabled` 不得携带 group_id。
> **多人复合字段**:目标 userkey 不在读回 map 中时停止自动更新,向用户说明需先通过页面配置人员范围;接口空成功后必须回读确认。
---
## 人员域
### user search
默认仅在职:
```bash
meegle user search --user-keys '["张三","李四"]' --project-key {{project_key}} --need-all-status false --format json
```
包含离职/停用:
```bash
meegle user search --user-keys '["张三"]' --project-key {{project_key}} --need-all-status true --format json
```
### user me
```bash
meegle user me --format json
```
---
## 工作台域
### mywork todo
```bash
meegle mywork todo --action todo --page-num 1 --asset-key {{asset_key}} --format json
```
`--action`:`todo` / `done` / `overdue` / `this_week`。
---
## 工时域
### workhour list-schedule
```bash
meegle workhour list-schedule --start-time 2025-03-01 --end-time 2025-03-31 --project-key 空间key --user-keys '["张三","李四"]' --work-item-type-keys '{{work_item_type_keys}}' --format json
```
### workhour list-records
```bash
meegle workhour list-records --project-key 空间key --work-item-type story --work-item-id 工作项ID --page-num 1 --format json
```
---
## 视图域
### view get
```bash
meegle view get --view-id 视图ID --project-key 空间key --fields '{{fields}}' --page-num {{page_num}} --format json
```
---
## 工作流域
### workflow get-node
```bash
meegle workflow get-node --work-item-id 工作项ID --field-key-list '{{field_key_list}}' --need-sub-task {{need_sub_task}} --page-num {{page_num}} --project-key 空间key --node-id-list '["节点ID或_all"]' --format json
```
### workflow transition(节点流)
```bash
meegle workflow transition --work-item-id 工作项ID --project-key 空间key --node-id 节点ID --action confirm --rollback-reason '{{rollback_reason}}' --format json
```
### workflow transition-state(状态流)
```bash
meegle workflow transition-state --work-item-id 工作项ID --project-key 空间key --transition-id 流转ID --format json
```
### workflow list-state-transitions
```bash
meegle workflow list-state-transitions --work-item-id 工作项ID --work-item-type story --user-key userkey --project-key 空间key --format json
```
### workflow list-state-required
```bash
meegle workflow list-state-required --work-item-id 工作项ID --state-key 目标状态key --project-key 空间key --mode unfinished --format json
```
---
## 评论域
### comment add
```bash
meegle comment add --work-item-id 工作项ID --content '评论内容' --project-key {{project_key}} --format json
```
### comment list
```bash
meegle comment list --work-item-id 工作项ID --project-key 空间key --page-num {{page_num}} --start-time {{start_time}} --end-time {{end_time}} --format json
```
---
## 关系域
### relation meta-definitions
```bash
meegle relation meta-definitions --project-key 空间key --work-item-type {{work_item_type}} --relation-work-item-type {{relation_work_item_type}} --format json
```
### relation list
```bash
meegle relation list --project-key 空间key --work-item-id 工作项ID --page-size {{page_size}} --relation-field-key {{relation_field_key}} --node-id {{node_id}} --relation-id {{relation_id}} --page-num {{page_num}} --format json
```
---
## 子任务域
### subtask update
```bash
meegle subtask update --node-id 节点ID --project-key {{project_key}} --task-id {{task_id}} --assignee '{{assignee}}' --work-item-id 工作项ID --role-assignee '{{role_assignee}}' --fields '{{fields}}' --schedule '{{schedule}}' --action create --deliverable '{{deliverable}}' --format json
```
references/attachment.md›
# 附件域
附件上传/下载分两步:先调 `attachment prepare-upload` / `attachment prepare-download` 申请带签名的对象存储 URL,再与对象存储做一次或多次 HTTP 直连。Meegle CLI 内置 `attachment +upload` / `attachment +download` 一键封装,把两步合成一条命令;脚本里需要逐步控制时也可单独调上面的 prepare 命令。
## attachment prepare-upload
申请上传签名。`work_item_id` 与 `work_item_type` **二选一必填**:已有工作项传 `work_item_id`;"创建工作项时同步上传附件" 场景传 `work_item_type`,两者同传时 `work_item_id` 优先。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --resource-type | number | 是 | 附件场景:13=评论附件 / 14=评论图片 / 15=工作项附件字段 / 16=富文本字段图片 |
| --file-name | string | 是 | 附件名称 |
| --mime-type | string | 是 | MIME 类型 |
| --size | number | 是 | 文件总大小(字节);后端据此判断走单次上传还是分片 |
| --work-item-id | string | 二选一 | 已有工作项 ID |
| --work-item-type | string | 二选一 | 工作项类型(仅 "创建工作项同步上传附件" 场景) |
| --field-key | string | 条件 | `resource_type=15/16` 必填,13/14 不填 |
## attachment prepare-download
申请下载签名。`file_url` 是其它命令(如 `workitem get` 的附件字段值、`comment list` 评论里的附件链接、富文本中的附件引用)回传的不透明引用,**不要**手工拼接。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
| --file-url | string | 是 | 附件 URL(来自附件字段、评论或富文本) |
## attachment +upload
端到端上传:CLI 在本地把 `attachment prepare-upload` 与对象存储的签名 HTTP POST 串起来,返回 `file_token` 与文件元数据,可直接喂给 `workitem create` / `workitem update` / `comment add` 的附件字段。**Meegle CLI 专用**。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `<source-path>`(位置参数) | string | 是 | 本地文件路径 |
| --resource-type | string | 是 | 13/14/15/16,含义同 `attachment prepare-upload` |
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 二选一 | 已有工作项 ID |
| --work-item-type | string | 二选一 | 创建场景的工作项类型 |
| --field-key | string | 条件 | resource_type=15/16 时必填 |
| --filename | string | 否 | 覆盖发送给后端的文件名(默认取本地 basename) |
| --content-type | string | 否 | 覆盖 MIME 类型(默认按扩展名探测,未识别走 `application/octet-stream`) |
## attachment +download
端到端下载:CLI 在本地把 `attachment prepare-download` 与对象存储的签名 HTTP GET 串起来,并用 `.partial` 临时文件 + 原子改名落盘,失败时不会留下半残文件。**Meegle CLI 专用**。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `<file-url>`(位置参数) | string | 是 | 附件 URL(来自附件字段、评论或富文本) |
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
| --output | string | 是 | 本地落地路径 |
| overwrite | bool | 否 | 目标已存在时是否覆盖(默认 false) |
references/auth-guard.md›
# Auth Guard(所有业务命令前必须执行)
## 触发条件
- **主动登录**:用户说"登录 Meegle"、"连接飞书项目"、"login meegle"等。
- **被动拦截**:用户请求任何 Meegle 业务操作(查询待办、查工作项、创建任务等),优先执行 Auth Guard。
- **URL 触发**:用户发送了飞书项目/Meegle URL。处理流程:
1. 先调 `url decode` 拿到结构化字段(`url_kind`、`host`、`simple_name`、`work_item_id` 等)。**禁止**自己从 URL 截取路径段作参数。字段含义与 kind 分支见 [url-kinds.md](url-kinds.md)。
2. 保存 `$url_host` = response.host、`$target_host = $url_host`、`$url_kind`、`$simple_name`、`$work_item_id`。`$url_host` 只表示原链接所属站点,`$target_host` 表示本轮业务必须连接的站点,两者都不得被后续登录状态覆盖。
3. 执行 Auth Guard(下面的 STEP 1 起)。
4. 登录成功后按 `$url_kind` 分支:
- `workitem_detail` → `project search` 得权威 `$project_key`,再 `workitem get` 查询详情
- `workitem_homepage` / `view_*` / `unknown` 等非详情页 → 按 url-kinds.md 的指引拒绝或追问
- 其他 kind → 参考 url-kinds.md 对应处理方式
按以下 STEP 顺序执行。每个 STEP 结尾的 GOTO 指明下一步,严格遵循跳转。
进入 STEP 1 前初始化变量:
- URL 触发 → 保留上面已解析的 `$url_host` 与 `$target_host`。
- 非 URL 触发 → SAVE `$url_host = null`;调用流程明确要求访问特定站点时,必须为本次请求传入 `$target_host`(例如链接生成流程中的显式域名),否则 SAVE `$target_host = null`。禁止继承上一请求的值。
- 将 `$profile_args` 保存为参数数组:用户显式选择 profile → `profile_args=(--profile "$profile")`;未显式选择 → `profile_args=()`。下面所有 Auth Guard 命令和 STEP DONE 的业务命令都必须复用同一个数组,禁止中途切换。
---
### STEP 1 — 检查登录状态
```bash
meegle "${profile_args[@]}" auth status --format json
```
返回值示例:
- 已登录:`{ "authenticated": true, "host": "meegle.com", "source": "token_store", "expires_in_minutes": 42 }`
- 未登录且有 host:`{ "authenticated": false, "host": "meegle.com", "source": null, "expires_in_minutes": null }`
- 未登录且无 host:`{ "authenticated": false, "host": null, "source": null, "expires_in_minutes": null }`
解析返回值,保存变量:
- `$authenticated` = response.authenticated
- `$auth_host` = response.host
**目标 host 一致性检查**:`$target_host` 非空时执行。比较前将 `$target_host` 与 `$auth_host` 的域名转为小写并去掉末尾的 `.`,端口必须保持一致。
- IF `$target_host != null` AND `$auth_host != null` AND 两者不一致 → SEND "当前登录站点为 `$auth_host`,但目标属于 `$target_host`。请切换或指定目标站点对应的 profile 后重试;本次未执行后续查询。";STOP
- IF `$auth_host != null` → SAVE `$host = $auth_host`
- IF `$auth_host == null` AND `$target_host != null` → SAVE `$host = $target_host`
- 其他情况 → SAVE `$host = null`
**跳转:**
- IF `$authenticated == true` → GOTO STEP DONE
- IF `$host != null` → GOTO STEP 2
- IF `$host == null` → GOTO STEP HOST
---
### STEP HOST — 选择站点
ASK user(等待用户回复):
> 你要连接哪个站点?
> 1) 飞书项目 (project.feishu.cn)
> 2) Meegle (meegle.com)
> 3) 自定义域名(请直接输入域名)
SAVE `$host` from user reply → GOTO STEP 2
---
### STEP 2 — OAuth 登录
```bash
meegle "${profile_args[@]}" auth login --host "$host"
```
命令会自动打开浏览器完成 OAuth 授权。等待命令执行完毕。
**跳转:**
- IF 命令成功(exit code 0) → GOTO STEP OK
- IF 命令失败 → SEND "OAuth 登录失败,请检查错误信息或在终端中手动重新执行上方登录命令",STOP
---
### STEP OK — 通知登录成功
SEND to user: "登录成功!"
> ⚠️ 此消息**必须单独发送**,不要与后续业务查询结果合并到同一条回复中。用户需要第一时间看到授权状态变化。
→ GOTO STEP DONE
---
### STEP DONE — 执行业务命令
Auth 已通过,执行用户请求的操作。每条业务命令继续使用进入 STEP 1 前保存的同一个 `$profile_args`。
## 错误处理
- 如果 bash 返回 `command not found` 或 npx 不可用,提示用户安装 Node.js 18+。
- 如果 OAuth 登录失败,提示用户在终端中手动重新执行上方登录命令。
references/cli-guide.md›
# CLI 使用指南
## 前置条件
运行环境需要 Node.js 18+。所有命令通过 `meegle` 执行。
## 命令结构
```bash
meegle <resource> <method> [flags] --format json
```
命令采用 `resource method` 两级结构。所有输出推荐使用 `--format json` 获取结构化数据。
## 全局 Flag
| Flag | 说明 |
|------|------|
| `--format json\|table\|ndjson` | 输出格式,默认 json |
| `--select <props>` | 选取输出属性,逗号分隔(支持 dot path,如 `name,owner.name`) |
| `--profile <name>` | 临时切换 profile |
| `--verbose` | 显示详细日志 |
| `--refresh` | 从服务端刷新本地命令缓存(旁路 24h cache) |
## 参数传递
几种方式,优先级从高到低:
1. **Flag 模式**(推荐):`--project-key PROJ --work-item-type story`
2. **--fields 模式**(写工作项字段,可重复):`--fields '{"field_key":"name","field_value":"任务标题"}' --fields '{"field_key":"priority","field_value":"1"}'`;`field_value` 支持任意 JSON 值(数组/对象原样传)
3. **--params 模式**(完整 JSON 兜底):`--params '{"fields":[{"field_key":"name","field_value":"任务标题"}]}'`
4. **--set 模式**(仅顶层参数快捷写法,不支持 fields[]):`--set page_num=1` 等价于 `--page-num 1`,支持 dot-path 嵌套;不要用它写工作项字段
Flag 覆盖 `--params`;`--set` 只影响顶层参数,**不会**写到 `fields[]`。
## 命令发现
CLI 的命令和参数会随版本更新。遇到不确定的命令或参数时,使用 `inspect` 获取最新信息:
```bash
meegle inspect # 列出所有可用命令
meegle inspect workitem.create # 查看具体命令的参数 schema
```
> 命令清单本地缓存 24 小时。如果 `inspect` 输出的参数与服务端实际不符,或服务端有新命令但 CLI 报 `unknown command`,加上 `--refresh` 强制从服务端重新拉取最新清单:
> ```bash
> meegle --refresh inspect workitem.create
> ```
## 输出处理
- 始终使用 `--format json` 获取结构化输出,方便解析
- 使用 `--select` 精简返回字段,如 `--select id,name,current_nodes.name`
- 命令返回错误时,JSON 中包含 `error` 和 `message` 字段
references/error-handling.md›
# 错误处理规则
> 症状 → 修复动作映射。语法/协议细节引用 [mql-syntax.md](mql-syntax.md) 与 SKILL.md「字段值格式」。
**通用原则**:从报错提取关键字 → 匹配下表修复 → 重试。**同一错误最多自动重试 2 次(共 3 次尝试)**。仍失败则停止自愈,向用户结构化说明:① 原始请求与目标语义;② 每次修复的关键改动与服务端返回;③ 推测根因(字段/角色/枚举不存在、无权限等)并请求澄清。**禁止无限重试或反复微调同一参数**。
---
## 1. MQL 自愈规则(高频,优先匹配)
> `workitem meta-fields` / `workitem meta-roles` 必须同时带 `--project-key` 和 `--work-item-type`;`workitem meta-types` 只需 `--project-key`。
### 1.1 元数据类
| 报错症状 | 修复动作 |
|---------|---------|
| 字段 key 在该空间+工作项类型下不存在 | 立即调 `workitem meta-fields`,同时传空间、类型与报错中的字段名,替换重试 |
| 字段中文名歧义(多字段同名) | 改用字段 key。通过 `workitem meta-fields` 的 `field_query` 拿所有匹配 key,选语义正确的替换;无法判断询问用户 |
| 把角色当字段写(如 `经办人`) | 见 [mql-syntax.md §12 角色](mql-syntax.md):`workitem meta-roles` 取 `role_name` / `role_id`,用 `` `__<role_name>` `` 或 fallback 复合列名 |
| 枚举值/状态值在该空间+工作项类型下不存在 | `workitem meta-fields` 精确获取对应字段的 options,用真实 label 替换。禁硬编码 |
| 树状字段值写成完整路径或非叶子 | `workitem meta-fields` 获取 options,确认叶子 `option_id` / label;父级查询改用 `any_match` |
| 查询不支持的字段类型(`attachment` / `file` / `spec_doc` / `specDocs`) | 从 SELECT/WHERE 移除,改用 `workitem get`。**`multi-file` 例外**:允许 `SELECT` / `IS NULL` / `IS NOT NULL`;深度筛选不支持 |
### 1.2 语法类
| 报错症状 | 修复动作 |
|---------|---------|
| 中文字符编码损坏 | 整个 MQL 用单引号包裹、中文名反引号、无多余反斜杠。重构重试 |
| 用了硬规则禁用语法(`SELECT *`、`count()`/`GROUP BY`、`REGEXP`、`CONTAINS()`) | 对照 [mql-syntax.md §1.1](mql-syntax.md)。`LIMIT ... OFFSET n` / `LIMIT n,count` 均支持,推荐配 `ORDER BY` |
| 字段名未加反引号导致 `syntax error near ':...'` | 字段名用反引号;含 `<target:xxx>` 时**整体**放同一对反引号(`` `name<target:all>` ``) |
| 顶层 SELECT/WHERE 用了 `` `name<target:all>` `` 报 `attribute[...] not found` | `<target:xxx>` **仅**允许在关系判断 lambda 内(`` x.`name<target:all>` `` 形式);顶层引用去掉修饰符 |
| 用 `\'` 转义单引号 | 改用 `''`(两个单引号) |
| `Internal % and _ characters must be escaped` | LIKE 内部字面量 `%` / `_` 必须写作 `\%` / `\_`;即便 `_` 出现在关键词中间(如 `%test_case%`)也会被服务端强制拒回,必须转义为 `%test\_case%`。见 [mql-syntax.md §1.6](mql-syntax.md) |
| LIKE 缺通配符 | 完整包含形态 `LIKE '%关键词%'` |
### 1.3 语义类
| 报错症状 | 修复动作 |
|---------|---------|
| FROM 缺空间或类型 | 修正为 `` FROM `project_key`.`work_item_type_key` `` |
| SELECT 中放了函数(`current_login_user()`、`array_contains()`) | 从 SELECT 移除,函数只能在 WHERE 中 |
| `parent_work_item() not supported in stage Where` | `parent_work_item()` 仅支持 SELECT;WHERE 中按父工作项过滤改用 `` any_relation_match(relation_field_chain('__父工作项'), x -> x.`work_item_id<target:all>` = '<父ID>') `` |
| 操作符与字段类型不兼容 | 对照 [mql-syntax.md §4 兼容性表](mql-syntax.md) 修正。常见:`number` / `bigint` 不支持 `BETWEEN` / `LIKE` / `array_contains`,改为 `>=` + `<=` / 精确等值 |
| 多值右值用了 JSON 数组字符串(`IN '["a","b"]'`) | `IN` 直接 syntax error,改元组 `IN ('a','b')`。`=` 服务端当前兼容 JSON 数组字符串但非推荐写法,统一改元组 `= ('a','b')`。**例外**:`array_intersect` / `risk_label() = ...` 保留 JSON 数组字符串 |
| `tree-multi-select` / `workitem_related_multi_select` 右值拒回 `metadata error` 或 `attrValueLabel not found`(如裸 option_id / 裸 work_item_id) | 改为 label 或 `<id:option_id>` / `<id:work_item_id>` 包裹形式。见 [mql-syntax.md §5.1](mql-syntax.md) |
| signal 字段 `IS NULL` / `IS NOT NULL` 报 `operator not supported` | signal 不支持空判断,改用 `!= '真实 label'` 或去掉该条件 |
| signal 值位传了 `option_id` / `<id:>` / `'true'` / `'false'` / `'null'` | 改用 `option_name` label(如 `'已通过'`) |
| Lambda 报 `lambda predicate operator not supported: <OP>` / `unsupported lambda predicate` | Lambda 内仅支持 `x = 'v'`、`x IN (...)`、同变量 OR。其余(`!=` / `NOT IN` / 比较符 / `LIKE` / `BETWEEN` / `IS NULL` / `RELATIVE_DATETIME_*` / `AND` 复合 / 嵌套 Match)全部拒回。见 [mql-syntax.md §14](mql-syntax.md)。多选/数组字段优先用顶层 `IN` / `NOT IN` / `array_contains` / `none_match`;仅值列表含 `team()` / `current_login_user()` 时用 `any_match` |
| `get_node_attribute('__BELONGING','状态')` 触发 nil pointer / panic | 见 [mql-syntax.md §15.6](mql-syntax.md) 权威规则:状态类改用 `=` 顶层比较(禁被 `array_contains` 包裹);`__BELONGING` 属性禁 AND 组合 |
### 1.4 参数类
| 报错症状 | 修复动作 |
|---------|---------|
| `RELATIVE_DATETIME_*` 报 `unexpected operator for future` / `invalid argument` | 见 [mql-syntax.md §6.1 兼容矩阵](mql-syntax.md):`_EQ` 只接受 `today`/`tomorrow`/`yesterday` 且**无 offset**;`_GT/_GE/_LT/_LE` 只接受 `today`(可带 `±Nd`);`future`/`past`+`Nd` 只允许配 `_BETWEEN` |
| ORDER BY 字段不支持排序 | 换可排序字段(如 `updated_at` / `start_time` / `work_item_id`)或移除 |
| MQL 与 session_id 都空 | 首查必须传 MQL;翻页必须传 session_id |
| 翻页参数缺失 | 传 `[{"group_id":"1","page_num":N}]`(无分组时 `group_id` 固定 `"1"`) |
| 未传 project_key | `workitem query` 的 `--project-key` 必填 |
| 空间不存在 | 用 `project search` 确认 |
| 空间名匹配多个 | 从报错提取候选,让用户选择或用精确 project_key 重试 |
| 当前用户对该空间无权限 | 告知用户需申请访问,**禁止重试** |
---
## 2. 通用字段值自愈
| 报错症状 | 修复动作 |
|---------|---------|
| `need STRING type, but got: LIST/MAP` | 原生 JSON 改为 JSON.stringify 字符串(见 SKILL.md「字段值格式」) |
| 字段值类型错配(数字↔字符串、单值↔数组) | 仅改格式,值不变 |
| 级联选项传非叶子节点 | 展示 `children` 树,让用户选叶子 |
| 枚举值不在可选项 | 从 options 匹配;唯一命中则修正重试,否则询问用户 |
---
## 3. 非 MQL 错误速查
| 报错症状 | 修复动作 |
|---------|---------|
| 找不到空间 / 中文名多命中 | `project search` 验证,取 project_key 精确调用 |
| 找不到工作项类型 | `workitem meta-types` 确认合法 `type_key` |
| 模板不存在/禁用 | `workitem meta-fields` 获取可用模板 |
| 角色查询无结果 / 经办人报告人查不到 | `workitem meta-roles` 确认 `role_name` / `role_id`;系统默认 `role_name`=`经办人`/`报告人`,`role_id`=`operator`/`reporter` |
| 人名→userkey 转换失败 | `user search` 批量查询 |
| 人员字段写入失败 | user 传单个 userkey;multi-user 必须 stringified 数组(如 `"[\"k1\",\"k2\"]"`) |
| 找不到节点 | `workflow get-node` 查全量节点详情列表匹配真实 `node_id`(`workitem get` 只返进行中节点) |
| 节点流转失败 | 节点流用 `workflow transition`;状态流用 `workflow transition-state`(先 `workflow list-state-transitions` 取 `transition_id`,`workflow list-state-required` 查必填) |
| 重复流转已完成节点 | 流转前 `workflow get-node` 检查状态 |
| 节点未激活 | 先确认工作流进度 |
| 创建缺模板 | `workitem meta-fields` 获取模板字段 |
| 创建必填字段未提供 | `workitem meta-create-fields` 取 `is_required=1` 的所有字段(注意 `workitem meta-fields` 不返回 required 标记) |
| 角色更新失败 | 改用 `workitem update` 的 `role_operate`(不走 fields) |
| `group_type=bind` 缺 `group_id` | 补非空 `group_id`;解绑用 `{"type":"disabled"}` |
| `group_type=auto/disabled` 却传了 `group_id` | 二选一:保留 `bind + group_id`,或去掉 `group_id` |
| `page_size` 被序列化为字符串 | Meegle CLI 改用 `--params '{"page_size":N,"page_token":"..."}'` |
| `mywork todo` 需选择工作区 | 从报错列表把 `asset_key`(Asset_xxx)传入重试 |
| 操作记录时间区间非法 | `start_time < end_time`,格式毫秒时间戳或 `YYYY-MM-DD` |
| 关联查询缺 `relation_id` | 先用 `relation meta-definitions` 获取 |
| 接口限流 | 等待 1-2 秒重试,或减少并发 |
| 视图配置失效 / session 缓存过期 | 前者 `view search` 取有效 ID;后者不传 `session_id` 重查 |
| 工作项已冻结/终止/归档 | 告知用户不可编辑,**禁止重试** |
| 状态流转目标不可达 | 先 `workflow list-state-transitions` 获取合法路径供用户选择 |
| 选项可见性不满足 | 重新用 `workitem meta-fields` 取上下文可见选项 |
| `work_item_type_key` 不存在 | `workitem meta-types` 取合法列表 |
| WBS 草稿不存在 | 先用 `wbs create-draft`,或确认该空间已启用 WBS |
| 非 IPD 项目调用 WBS/资源库/交付物 | 告知用户该空间不支持 |
| `wbs edit-draft` 父行不支持新增子行 | `wbs list-draft-rows` 确认父行类型和拆解模式 |
| 工时未启用 | 需在项目设置开启(`actual_work_time_switch=true` 或安装"工时登记"插件),**禁止重试** |
| 工时记录返回空但确认有数据 | 确认 `work_item_type` 是否正确 |
---
## 4. 熔断条件(立即终止)
- 空间未找到(`project search` 连续 3 次失败)
- Permission Denied(无空间访问权限)
- 服务端返回明确的"已归档/已冻结/无权限"业务错误
references/field-value-extras.md›
# 字段值进阶:关联工作项名称 → ID 转换
当用户为 `workitem_related_select` / `workitem_related_multi_select` 字段提供的是**工作项名称而非 ID** 时,按以下流程转换后再写入。
1. **获取关联字段的目标约束**:从 `workitem meta-fields` 返回的该字段配置中,提取其绑定的**目标空间**(`project_key`)和**目标工作项类型**(`work_item_type_key`)。若配置未限定(可关联任意类型),默认在当前空间内搜索。
2. **按名称搜索目标工作项**:调用 `workitem query`,在目标空间和类型范围内按名称匹配。示例 MQL:
```sql
SELECT `工作项ID`, `名称` FROM `目标空间`.`目标类型` WHERE `名称` = '用户给的名称'
```
精确匹配无结果时改用 `like '%关键词%'` 模糊搜索。
3. **消歧处理**:
- 唯一结果 → 直接取工作项 ID
- 多个结果 → 列出所有匹配项(ID + 名称 + 状态)让用户确认
- 零结果 → 提示用户"未找到名为 XXX 的工作项,请确认名称或直接提供 ID"
4. **写入格式**:
- `workitem_related_select` → 传入单个 ID 字符串
- `workitem_related_multi_select` → 传入 stringified ID 数组
- 不同空间可能要求字符串或数字格式,遇类型校验失败立刻切换格式重试
5. **循环引用保护**:写入前必须排查当前工作项自身 ID,**禁止将自身 ID 写入关联字段**,否则会触发 `exists loop`(循环引用)报错。
references/misc.md›
# 其它低频命令
低频/单命令小域的参数表汇总。涵盖团队、图表、子任务、关系、评论查询、工时记录、交付物、资源库、WBS 辅助命令。
---
## 团队
### team list
查看空间下的团队列表。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 否 | 空间 key |
### team list-members
查看团队成员列表。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --team-id | string | 是 | 团队 ID |
---
## 度量图表
### chart get
查看图表详情。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --chart-id | string | 是 | 图表 ID |
### chart list
查看视图下的图表列表。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --view-id | string | 是 | 视图 ID |
---
## 子任务
### subtask update
创建/修改/完成/回滚子任务。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --node-id | string | 是 | 节点 ID |
| --work-item-id | string | 是 | 工作项 ID |
| --action | string | 是 | create/update/confirm/rollback |
---
## 关系
### relation list
查看关联的工作项列表。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
| --relation-field-key | string | 否 | 关联关系字段 key,从 `relation meta-definitions` 获取 |
| --relation-id | string | 否 | 关联关系 ID,从 `relation meta-definitions` 获取 |
| --node-id | string | 否 | 节点 ID,查询某节点下的关联时传入 |
| --page-num | number | 否 | 分页页码,从 1 开始 |
| --page-size | number | 否 | 每页数量,最大 50 |
### relation meta-definitions
查看空间下的关联关系定义。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
---
## 评论查询
### comment list
查看评论列表。添加评论用 `comment add`(见 SKILL.md 主文件)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
---
## 工时记录
### workhour list-records
查看工作项的工时登记记录。团队排期用 `workhour list-schedule`(见 SKILL.md 主文件)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-type | string | 是 | 工作项类型 |
| --work-item-id | string | 是 | 工作项 ID |
---
## 交付物
### deliverable list
查看交付物详情及其所属根工作项 / 来源工作项。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-ids | string[] | 否 | 工作项 ID 列表;URL 自动解析;提供名称需先调 `workitem get` 拿 ID |
---
## 资源库
### resource create
在已启用资源库的工作项类型下创建资源模板(资源实例)。先调 `resource meta-fields` 取字段 / 角色配置。
**创建资源实例 vs 从资源实例创建普通工作项**:
- 创建资源实例:使用 `resource create`;`work_item_type_key` 表示资源库启用的工作项类型,`template_id` 表示流程模板,`fields` / `roles` 描述新资源实例自身。
- 从资源实例创建普通工作项:这是“基于已有资源实例派生/创建业务工作项”的语义,参数通常需要源资源实例标识。当前命令参数必须以 `inspect` / schema 为准;若没有显式源资源实例参数,不要把源资源实例 ID 塞进 `work_item_type_key`、`template_id` 或普通字段里猜测调用。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-type-key | string | 是 | 工作项类型 key 或名称;失败时先调 `workitem meta-types` |
| --fields | object[] | 否 | 资源字段列表,每项含字段 key 与字段值 |
| --roles | object[] | 否 | 角色人员;为空则不指定 |
| --template-id | string | 否 | 工作流模板 ID 或名称;未传则取该工作项类型的第一个流程模板 |
### resource meta-fields
查看资源库的字段 / 角色配置。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-type-key | string | 是 | 工作项类型 key 或名称 |
---
## WBS 辅助命令
> 计划表(WBS)的核心查询 / 编辑 / 发布命令见 [wbs.md](wbs.md)。本节仅收 4 个辅助命令:草稿生命周期管理(create-draft / reset-draft)、异步操作进度查询(get-draft-progress)、流程资源库元素查询(list-element-templates)。
### wbs create-draft
为指定工作项实例创建新的计划表草稿。当需要编辑计划表但当前不存在草稿时,先调本工具创建草稿,再配合 `wbs edit-draft` 进行编辑。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID,单值;URL 自动解析 |
| --project-key | string | 是 | 空间 key |
### wbs reset-draft
将草稿重置为线上实例状态,**放弃所有未发布的修改**。不传 `uuids` 时全量重置。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID |
| --project-key | string | 是 | 空间 key |
| --uuids | string[] | 否 | 要重置的行 uuid 列表;为空则全量重置 |
### wbs get-draft-progress
查询计划表草稿异步操作(create / edit / publish / reset)的执行进度。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID 或名称 |
| --op-type | string | 是 | 操作类型:`create` / `edit` / `publish` / `reset` |
| --operation-id | string | 是 | 操作 ID(由 create-draft / edit-draft / publish-draft / reset-draft 返回) |
### wbs list-element-templates
列出流程资源库中的资源节点(node)或资源任务(task)模板。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --element-type | string | 是 | 资源库类型:`node` 或 `task` |
| --project-key | string | 是 | 空间 key |
| --work-item-type | string | 是 | 工作项类型 key 或名称 |
| --page-size | number | 否 | 页大小 |
| --page-no | number | 否 | 页码 |
references/mql-syntax.md›
# MQL 语法规范
> `workitem query` 的 `--mql` 参数必须是完整 SQL 语句,包含 `SELECT` + `FROM`。禁止 JSON 对象、条件片段、缺 SELECT 的写法。
---
## 1. 硬规则
### 1.1 禁用语法
| 禁止 | 替代 | 原因 |
|------|------|------|
| `SELECT *` | 显式列出字段 | 不支持通配符 |
| `count(*)` / `SUM()` / `GROUP BY` | 从返回 `count` 字段读总数 | 不支持聚合 |
| `REGEXP` / `regexp_like()` | `LIKE '%...%'` | 不支持正则 |
| `currentUser()` / `current_user()` | `current_login_user()` | 函数名错误 |
| `CONTAINS(field, val)` | `array_contains()` 或 `LIKE` | 无此运算符 |
| 日期无引号 `2026-06-01` | `'2026-06-01'` | 会被解析为减法 |
### 1.2 不推荐但服务端受理
以下语法**服务端行为不稳定**(部分字段类型直接报 `operator not supported`),一律禁止生成:
| 语法 | 服务端行为 | 强制改写为 |
|------|-----------|-----------|
| `NOT BETWEEN a AND b` | 语法受理 | `< a OR > b` |
### 1.3 不可查询的字段类型
以下字段类型出现在 `SELECT` / `WHERE` 中会报 `unsupported field type`,必须移除并改用 `workitem get`:
- `attachment` / `file`(附件)
- `spec_doc` / `specDocs` / `spec_documents`(文档)
**例外**:`multi-file`(多文件,如 `multi_attachment`)**仅支持** `SELECT` / `IS NULL` / `IS NOT NULL`,返回 `key_label_value_list`。不支持 `LIKE` / `array_contains` 等深度筛选。
### 1.4 常见字段名易错映射
MQL 支持字段 key 和中文名/label,**必须优先 key**。中文名多存在同名歧义。
| ❌ 写法 | ✅ 替代 |
|--------|--------|
| `state_key` / `status` | `work_item_status` |
| `archiving_status` / `is_archived` | `archiving_date`(未归档用 `IS NULL`) |
| 中文名如「名称」「当前负责人」「创建时间」 | 先 `workitem meta-fields` 查 key |
除上表外,中文名一律先用 `workitem meta-fields` 查询,并同时传 `--project-key` 与 `--work-item-type`。
### 1.5 枚举值/状态值禁止硬编码
状态、select、tree-select 等枚举 label 由「空间 + 工作项类型」自定义。**禁止硬编码** `关闭`、`已完成`、`进行中`、`OPEN`、`CLOSED` 等。违反报 `attrValueLabel not found`。
**流程**:`workitem meta-fields` 取 options → 用真实 label 或 `<id:option_id>` 写入。
### 1.6 LIKE 转义
- LIKE 通配符**仅 `%`(任意长度)**。`_` **不是**通配符:未转义会被服务端强制拒回 `Internal % and _ characters must be escaped`(含 `%foo_bar%` / `%test_case%` 等所有含裸 `_` 的模式)
- 字面量 `_` 或"任意单字符"语义**必须**写 `\_`;同理 `%` 若作字面量必须 `\%`
- 模式必须完整包含形态 `'%...%'`
```sql
✅ WHERE `name` LIKE '%性能问题%'
✅ WHERE `name` LIKE '%test\_case%' -- 字面量下划线必须转义
✅ WHERE `name` LIKE '%100\%完成%'
❌ WHERE `name` LIKE '100%完成' -- 缺前 %
❌ WHERE `name` LIKE '%test_case%' -- 裸 _ 未转义,服务端拒回
❌ WHERE `name` LIKE '%100%完成%' -- 内部 % 未转义
```
### 1.7 字符串编码
- `--mql` 参数外层用单引号包裹
- MQL 内部字符串值用单引号;嵌套单引号用 `''`(两个单引号)
- **禁止**用 `\` 转义单引号(服务端语法接受但**不做转义**:`'张\'三'` 会被当作字面量 `张\`,导致空结果)
- 中文字段名/值放在反引号或单引号内
### 1.8 多值右值语法
多值右值**统一用元组** `(v1, v2)`,适用于 `IN` / `NOT IN` / `array_contains` / `=` / lambda 内的 `IN`。**不推荐** JSON 数组字符串 `'["a","b"]'`:`IN` 直接 syntax error;`=` 服务端当前兼容但非推荐写法,统一用元组。
**唯一例外**:控件函数场景要求 JSON 数组字符串——`array_intersect(<控件函数>, '["a","b"]')`(有交集语义)与 `risk_label() = '["a","b"]'`(集合完全相等语义)。
---
## 2. 基础语法
```sql
SELECT fieldList -- 字段列表,禁 *
FROM `project_key`.`work_item_type` -- 必须双段完整
WHERE conditionExpression -- 可选
[ORDER BY field [ASC|DESC]]
[LIMIT [offset,] row_count] -- 也支持 LIMIT row_count OFFSET n
```
**标识符**:
- 字段/表名推荐反引号:`` `work_item_id` `` / `` `project_key`.`story` ``
- `<target:xxx>` 修饰符**必须整体放在同一对反引号内**:`` `name<target:all>` ``。写成 `` `name`<target:all> `` 或 `name<target:all>` 报 `syntax error near ':...'`
- `<target:xxx>` **仅允许在关系判断 lambda 内部**(`` x.`name<target:all>` `` 形式)。顶层 SELECT/WHERE 直接引用主表字段禁止使用,否则报 `attribute[name<target:all>] not found`
- 字符串值用单引号;枚举值优先用 label(必须先经 workitem meta-fields 确认存在),找不到时用 `<id:option_id>`
---
## 3. 数据类型
| MQL 类型 | 对应字段类型 |
|---------|-------------|
| bool | bool |
| bigint | number 中的 `work_item_id`、`auto_number` |
| double | 其它 number |
| varchar | text、multi-pure-text、multi-text、select、tree-select、radio、user、link、signal、workitem_related_select |
| date | date(格式 `YYYY-MM-DD` 或 `YYYY-MM-DD+TZD`) |
| datetime | schedule、precise_date(格式 `YYYY-MM-DDThh:mm:ss[TZD]`) |
| array(varchar) | multi-select、tree-multi-select、multi-user、link_cloud_doc、workitem_related_multi_select |
| array(struct) | compound_field |
| lambda | `x -> x IN ('a','b')` 等 |
---
## 4. 运算符与字段类型兼容性
**唯一权威表**。违反本表报 `field not supported OPERATOR` / `operator not supported`。
| 字段类型 | 支持 | 不支持 |
|---------|------|--------|
| text / multi-pure-text | `=` `!=` `IN` `NOT IN` `LIKE` `NOT LIKE` `IS NULL` `IS NOT NULL` | 比较符、`BETWEEN`、`array_contains` |
| multi-text | `LIKE` `NOT LIKE` `IS NULL` `IS NOT NULL` | 其它 |
| select / radio / workitem_related_select | `=` `!=` `IN` `NOT IN` `IS NULL` `IS NOT NULL` | `LIKE`、比较符、`BETWEEN`、`array_contains`、`any_match` |
| tree-select(单选) | `=` `!=` `IN` `NOT IN` `IS NULL` `IS NOT NULL` | `LIKE`、比较符、`BETWEEN`、`array_contains`、`any_match`(不支持父级级联,仅按叶子精确匹配) |
| user | `=` `!=` `IN` `NOT IN` `array_contains` `IS NULL` `IS NOT NULL` | `LIKE`、比较符、`BETWEEN` |
| link | `=` `!=` `LIKE` `NOT LIKE` `IS NULL` `IS NOT NULL` | `IN`、比较符、`BETWEEN`、`array_contains` |
| signal | `=` `!=` `IN` `NOT IN`(值位见下) | `IS NULL` / `IS NOT NULL` 报 `operator not supported`;其它 |
| number(含 `work_item_id`) | `=` `!=` `IN` `NOT IN` `>` `>=` `<` `<=` `IS NULL` `IS NOT NULL` | `LIKE`、`BETWEEN`、`array_contains` |
| array(varchar) | `=` `!=` `IN` `NOT IN` `array_contains` `NOT array_contains` `any_match` `none_match` `IS NULL` `IS NOT NULL` | `LIKE`、比较符、`BETWEEN` |
| date | `=` `!=` `>` `>=` `<` `<=` `BETWEEN` `RELATIVE_DATETIME_*` `IS NULL` `IS NOT NULL` | `LIKE`、`IN`、`array_contains` |
| datetime(`schedule` / `precise_date`) | `>` `>=` `<` `<=` `BETWEEN` `RELATIVE_DATETIME_*`(需先拆为子字段) `IS NULL` `IS NOT NULL` | `=` `!=`、`LIKE`、`IN`、`array_contains`;必须拆为 `` `__字段key_开始时间` `` / `` `__字段key_结束时间` `` 访问(见 §13.1) |
| bool | `=` `!=` `IS NULL` `IS NOT NULL` | `IN`、`LIKE`、`BETWEEN`、`array_contains`、比较符 |
| array(struct)(`compound_field`) | 不可直接在 WHERE 中比较 | 需通过子字段或 `workitem get` 读取 |
| `multi-file` | `SELECT` / `IS NULL` / `IS NOT NULL` | `LIKE`、比较符、`BETWEEN`、`array_contains`、`IN` |
**signal 值位(MQL 查询)**:仅接受 `option_name` label(如 `'已通过'` / `'未通过'` / `'处理中'` / `'暂无信息'`)。禁 `option_id`(`'passed'`)、`<id:option_id>`、`'true'/'false'/'null'`。写入接口(create/update/transition)的值位规则不同,见 [SKILL.md「字段值格式」](../SKILL.md)。
**数组字段语义**:`=` / `!=` 按整组精确匹配(数组完全等于右值集合);`IN` 按元素级 OR(存在任一即匹配);`array_contains` 多参数为 AND(同时包含所有元素);`any_match` 按逐元素匹配。
**树状字段父级匹配**:`tree-multi-select`(数组型)父级匹配(含所有下级)用 `any_match(field, x -> x = '<父级 label>')` 或 `` `field` IN ('<父级 label>') ``;`array_contains` 仅做精确 label 匹配、**不含子级级联**。`tree-select`(单选)不支持父级级联,仅按叶子 label / option_id 精确匹配。
**tree-multi-select / workitem_related_multi_select 右值形式**:仅接受 label 或 `<id:option_id>` / `<id:work_item_id>` 包裹形式。裸 `option_id` / `work_item_id` 拒回 `metadata error`。详见 §5.1。
> **`workitem_related_select`** 值位:`= '<id:work_item_id>'` 或真实 label 命中;裸 `work_item_id` 拒回 `attrValueLabel not found`;`IS NULL` / `IS NOT NULL` 可用。
---
## 5. 数组与集合函数
### 5.1 函数清单
| 函数 | 说明 |
|------|------|
| `array_contains(field, e1 [,e2,...])` | 数组包含元素;多参数为 AND(同时包含所有元素)。对 `tree-multi-select` / `workitem_related_multi_select` 字段,右值仅接受 label 或 `<id:option_id>` / `<id:work_item_id>` 包裹形式;裸 `option_id` / `work_item_id` 拒回 `attrValueLabel not found` 或 `metadata error` |
| `any_match(field, x -> pred)` | 任一元素满足;**仅当 lambda 值列表含函数(`team()` / `current_login_user()`)或多个 `<id:userkey>` 时使用** |
| `none_match(field, x -> pred)` | 全部元素都不满足 |
| `array_intersect(field, '[...]')` | 有交集;**仅用于控件函数返回值**(`risk_label()`、`all_nodes_name()`、`in_progress_nodes_name()`);对普通多选字段会报 `array_intersect: invalid right_array`。**第二参数必须是 JSON 数组字符串** |
### 5.2 多选/数组字段决策顺序
1. 单元素 / 多元素 OR → `IN + 元组`
2. 多元素 AND(同时包含)→ `array_contains(field, 'a', 'b')`
3. 集合完全相等 → `` `field` = ('v1','v2') ``(元组,禁 JSON 数组字符串)
4. 全不属于 → `NOT IN` 或 `none_match`;`NOT array_contains` 语义为"不同时包含"(至少缺一个),非"全不属于"
5. **树状/级联字段父级匹配**(含所有下级):`tree-multi-select`(数组型)用 `any_match(field, x -> x = '<父级 label>')` 或 `` `field` IN ('<父级 label>') ``;`array_contains` 仅精确匹配、不含子级级联。`tree-select`(单选)不支持父级级联,仅按叶子 label / option_id 精确匹配
6. 值列表含 `team()` / `current_login_user()` 等返回集合的函数,或需在集合内做多个 `<id:userkey>` 判断 → `any_match(x -> x IN (...))`
```sql
-- 单人命中(多人字段)
array_contains(`current_status_operator`, '<id:userkey>')
-- 多人 OR
`current_status_operator` IN ('<id:k1>', '<id:k2>')
-- 多标签 AND
array_contains(`tag`, '标签A', '标签B')
-- 空判断
`tag` IS NULL
-- 值列表含团队函数
any_match(`watchers`, x -> x IN (team(true, '真实团队名')))
```
---
## 6. 时间函数 `RELATIVE_DATETIME_*`
签名:`RELATIVE_DATETIME_{EQ|GT|GE|LT|LE|BETWEEN}(col_name, 'date_para', ['days'])`
**date_para**:`today` / `tomorrow` / `yesterday` / `current_week` / `next_week` / `last_week` / `current_month` / `next_month` / `last_month` / `future` / `past`。
### 6.1 函数 × date_para × days 兼容矩阵
不在允许列的组合服务端拒回 `unexpected operator for <para>` / `invalid argument`。
| 函数 | **推荐使用** date_para | days(`'Nd'` / `'-Nd'`) | 备注 |
|------|-----------------------|--------------------------|------|
| `_EQ` | `today` / `tomorrow` / `yesterday` | ❌ 不接受 | `_EQ + future/past` **无 offset** 时报 `invalid argument: relative datetime <para> expr must have 2 params`;带 offset 时服务端受理但语义模糊,**禁止生成**(`future`/`past` 语义应改用 `_BETWEEN`) |
| `_GT` / `_GE` / `_LT` / `_LE` | 仅 `today` | ✅ 接受,可正可负 | — |
| `_BETWEEN` | `current_week` / `next_week` / `last_week` / `current_month` / `next_month` / `last_month` / `future` / `past` | 仅 `future` / `past` 接受且**必须传** `'Nd'`;其它 date_para 不接受 days | ⚠️ 服务端**拒回** `today` / `tomorrow` / `yesterday`(`unexpected operator for today`),改用 `_EQ` |
### 6.2 示例
```sql
-- 今天创建
RELATIVE_DATETIME_EQ(`start_time`, 'today')
-- 上周创建
RELATIVE_DATETIME_BETWEEN(`start_time`, 'last_week')
-- 未来 3 天到期
RELATIVE_DATETIME_BETWEEN(`field_xxxxxx`, 'future', '3d')
-- 今天前后 N 天
RELATIVE_DATETIME_GT(`start_time`, 'today', '-3d')
RELATIVE_DATETIME_LT(`start_time`, 'today', '3d')
```
字段 key 必须先经 `workitem meta-fields` 确认,且同时传 `--project-key` 与 `--work-item-type`;禁复用示例 key。
---
## 7. 人员与角色函数
| 函数 | 返回 |
|------|------|
| `current_login_user()` | 当前登录用户 userkey |
| `team(include_manager, '团队名')` | 团队成员 userkey 数组;首参 `true` 含管理者 |
| `all_participate_persons()` | 全部参与人 userkey 数组 |
| `participate_persons()` | 当前参与人 userkey 数组 |
| `participate_roles()` | 参与角色的 `role_name` label 数组 |
**关键约束**:
- 团队名必须先用 `team list` 查询,并传 `--project-key`;否则报 `attribute_value not found`
- `participate_roles()` 值位**必须**用 `role_name` label(`'后端开发'`),禁传 `role_id`(`'fe_rd'`)
- 人员字段值位**必须**用 `<id:userkey>`(详见 §11)
```sql
-- 当前负责人是我
array_contains(`current_status_operator`, current_login_user())
-- 指派给某团队(含管理者)
any_match(`current_status_operator`, x -> x IN (team(true, '真实团队名')))
-- 有指定角色参与
array_contains(participate_roles(), '后端开发', '前端开发')
```
---
## 8. 节点函数
| 函数 | 用途 |
|------|------|
| `all_nodes_name()` | 全部节点名数组 |
| `in_progress_nodes_name()` | 进行中节点名数组 |
| `risk_label()` | 节点延期状态标识数组,**不支持 `any_match`** |
| `get_node_attribute(node, attribute)` | 指定节点属性;`node` 可为节点名/`__ALL`/`__BELONGING`;**「所属节点」必须用 `__BELONGING`** |
### 8.1 `get_node_attribute` 属性访问
**可用属性**:排期、估分、节点时间、节点完成结论、节点完成意见、负责人、当前负责人、状态。**指定节点负责人**:`owner` 与 `负责人` 语义等价。
**开始/结束时间访问形式**(作为第二参数):
- 节点排期:`'__排期_开始时间'` / `'__排期_结束时间'`
- 节点时间:`'__节点时间_开始时间'` / `'__节点时间_结束时间'`
### 8.2 `__ALL` 简写规则
- 对整体属性直接用区间/比较;**禁止**拆 `__排期_开始时间` / `__排期_结束时间`,**禁止**外套 `any_match`
- `排期` 支持 `BETWEEN`;`估分` 不支持 `BETWEEN`,需拆 `>= a AND <= b`
```sql
-- 指定节点负责人(value 必须 <id:userkey>)
WHERE array_contains(get_node_attribute('需求详评','owner'), '<id:userkey>')
-- 开始节点排期在过去 30 天内
WHERE RELATIVE_DATETIME_BETWEEN(get_node_attribute('开始','__排期_开始时间'), 'past','30d')
-- 所属节点当前负责人
WHERE array_contains(get_node_attribute('__BELONGING','当前负责人'), '<id:userkey>')
-- 全部节点排期在区间内
WHERE get_node_attribute('__ALL','排期') between '2026-01-01' and '2026-03-31'
-- 全部节点估分 ≥ 30
WHERE get_node_attribute('__ALL','估分') >= 30
```
节点名以 `workflow get-node` 返回为准:`--node-id-list` 传 `["_all"]`,并同时传 `--project-key` 与 `--work-item-id`。
### 8.3 控件函数多值语义
**`all_nodes_name()` / `in_progress_nodes_name()`**(不能用 `IN`,只能走 `any_match`):
| 语义 | 写法 |
|------|------|
| 存在选项属于(OR) | `any_match(<控件>, x -> x IN ('a','b'))` |
| 包含(AND) | `array_contains(<控件>, 'a','b')` |
| 集合完全相等 | `<控件> = '["a","b"]'`(JSON 数组,例外) |
| 全部不属于 | `none_match(<控件>, x -> x IN ('a','b'))` |
| 不同时包含(至少缺一个) | `NOT array_contains(<控件>, 'a','b')` |
**`risk_label()`**(**不支持 `any_match`**):
| 语义 | 写法 |
|------|------|
| 有交集(默认「含延期节点」) | `array_intersect(risk_label(), '["延期/前端","延期/后端"]')` |
| 全部包含(AND) | 多个 `array_contains(risk_label(), 'x')` AND |
| 父级「延期」/「排期信息不全」 | `array_contains(risk_label(),'延期')` |
---
## 9. 关系与关联函数
### 9.1 关系判断函数
对关系对端做条件判断:
- `any_relation_match(rel, x -> expr)` — 存在一个对端满足
- `all_relation_match(rel, x -> expr)` — 每一个对端都满足
- `none_relation_match(rel, x -> expr)` — 每一个对端都不满足
- `not all_relation_match(rel, x -> expr)` — 至少一个对端不满足
`relation_field_chain` 等取关系后**必须**外层套关系判断函数。
### 9.2 关系参数三种形式
1. 关联字段:`` `字段key` ``
2. `relation('关系名')`
3. `relation_field_chain('rel1', 'rel2' [, 'rel3'])` — ≤ 3 跳
**子任务父工作项**:关系名固定 `'__父工作项'`(双下划线前缀)。报错 `relationNode not found, label:父工作项` 时改为 `'__父工作项'`。
### 9.3 跨端字段引用
对端字段引用形式:`` x.`字段名<target:project_key::type_key>` `` 或 `` x.`字段名<target:all>` ``。
**通用字段必须用 `<target:all>`**:标题、创建人、创建时间、业务线、优先级、当前负责人、所属工作项、所属空间、工作项 ID、工作项类型、状态。
```sql
-- 对端优先级为 P0
WHERE any_relation_match(`多选关联字段`, x -> x.`priority<target:all>` = 'P0')
-- 子任务父工作项名称
WHERE any_relation_match(relation_field_chain('__父工作项'), x -> x.`name<target:all>` like '%登录%')
-- 多级关系 + 节点属性
WHERE all_relation_match(relation_field_chain('__父工作项','需求关联软件'), x -> array_contains(get_node_attribute('开始','负责人'), '<id:userkey>'))
```
### 9.4 其它关系函数
- `parent_work_item(relation('关系名'))` — 父工作项 ID。**仅支持 SELECT 阶段**,WHERE 中使用报 `parent_work_item() not supported in stage Where`。
- SELECT:`SELECT parent_work_item(relation('关系名')), work_item_id FROM ...`
- 按父工作项过滤改用:`WHERE any_relation_match(relation_field_chain('__父工作项'), x -> x.\`work_item_id<target:all>\` = '12345')`
- `association()` — 跨空间关联实例 ID:`WHERE association() = '实例ID'`
- `linked_work_item()` — 子任务来源控件(父工作项 ID)。判空推荐 `IS NOT NULL`;等值右值必须是真实父工作项 ID,否则 `attribute_value not found`
---
## 10. 状态函数 `status_time`
对状态流工作项(如 `issue`、缺陷)和节点流工作项(如 `story`)均有效。传入的状态名不存在时报 `metadata error`,需以 `workflow list-state-transitions` 或 `workitem meta-fields` 返回的真实状态名为准;后者的 `--field-keys` 传 `["work_item_status"]`。
| 用法 | 允许位置 | 参数形式 |
|------|---------|---------|
| `status_time('状态名')` | WHERE / SELECT | **纯状态名**,不带 `__` 前缀 或 `_开始时间`/`_结束时间` 后缀 |
| `status_time('__状态名_开始时间')` / `_结束时间` | **仅 SELECT 或时间差表达式** | WHERE 中使用会报错 |
状态名以 `workflow list-state-transitions` 或 `workitem meta-fields` 返回的真实 option 为准;后者的 `--field-keys` 传 `["work_item_status"]`。
```sql
-- ✅ WHERE 用纯状态名过滤
WHERE status_time('<状态名>') between '2025-01-01' and '2025-12-31'
-- ✅ SELECT 计算状态累计时长
SELECT `work_item_id`, status_time('__<状态名>_结束时间') - status_time('__<状态名>_开始时间')
FROM `project_key`.`issue`
-- ❌ WHERE 里做算术过滤
WHERE status_time('__<状态名>_结束时间') - status_time('__<状态名>_开始时间') > 86400
```
---
## 11. 名称消歧 `<id:xxxx>`
### 11.1 人员字段值位规则
人员字段、user、multi-user、自定义人员控件、角色列的值位**必须**优先用 `<id:userkey>`:
- 裸 userkey(`= 'example_userkey'`)在部分场景被当姓名解析,报 `user label '...' does not exist`
- 裸中文姓名(`= '张三'`)常报 `user label '...' is not unique`(即便 `user search` 唯一,服务端仍全局校验)
流程:`user search` 拿 userkey → 值位写 `<id:userkey>`。
```sql
-- 单人字段
WHERE `owner` = '<id:userkey>'
-- 多人字段:= 或 array_contains 均可
WHERE array_contains(`current_status_operator`, '<id:userkey>')
-- 多人 OR:首选 IN;含函数返回集合时才用 any_match
WHERE `current_status_operator` IN ('<id:k1>', '<id:k2>')
WHERE `current_status_operator` = current_login_user()
```
### 11.2 团队/枚举消歧
同名重复时用 `<id:xxxx>`:
```sql
WHERE any_match(`current_status_operator`, x -> x IN (team(true, '开放平台团队<id:3455>')))
WHERE `priority` = '<id:option_2>'
```
---
## 12. 角色(Role)
**角色不是字段**。MQL 中角色作为**列引用**。
### 12.1 列名两种写法
| 写法 | 使用场景 | 示例 |
|------|---------|------|
| **主写法** `` `__<role_name>` `` | 默认。`role_name` 严格取自 `workitem meta-roles` 返回值,含空格原样保留 | `` `__后端开发` ``、`` `__经办人` `` |
| **fallback** `` `__role_<project_key>_<work_item_type>_<role_id>` `` | 仅当 `role_name` 与其它字段/角色中文名冲突时 | `` `__role_<project_key>_story_<role_id>` `` |
**硬性前置**:涉及角色的 MQL 必须先调 `workitem meta-roles` 获取真实 `role_name` / `role_id`,并同时传 `--project-key` 与 `--work-item-type`。**禁止**按用户自然语言原文(俗称、缩写、错别字)直接拼列名。
**系统默认角色**:`role_name` = 「经办人」/「报告人」;`role_id` = `operator` / `reporter`(后者仅用于 `role_operate` API 参数,禁出现在 MQL 列名或函数里)。
### 12.2 无效形式(Code 3010 `attr label not found`)
- `` `__<role_id>` ``(如 `__fe_rd`、`__operator`)
- `` `<role_id>` ``(无 `__` 前缀)
- 函数包装:`role(fe_rd)`、`get_role_owners(fe_rd)`
```sql
-- ✅ 主写法
WHERE array_contains(`__后端开发`, '<id:userkey>')
WHERE array_contains(`__经办人`, '<id:userkey>')
-- ✅ fallback(仅冲突时)
WHERE array_contains(`__role_<project_key>_story_<role_id>`, '<id:userkey>')
-- ❌ 缺 __ / 用 role_id / 中文原文
WHERE `经办人` = '<id:userkey>'
WHERE array_contains(`__operator`, '<id:userkey>')
```
---
## 13. 特殊字段查询
### 13.1 日期区间字段(date_range)
工作项级日期区间字段(自定义"计划周期"等)**不能**直接以字段 key 查询,必须拆成访问形式(**MQL 派生语法**,非复合字段子字段概念):
- `` `__<字段key>_开始时间` ``
- `` `__<字段key>_结束时间` ``
> 与节点排期不同:节点排期用 `get_node_attribute(...,'__排期_开始时间')` 访问。
```sql
-- ✅ 正确
WHERE `__field_xxxxxx_开始时间` > '2025-01-01'
WHERE RELATIVE_DATETIME_BETWEEN(`__field_xxxxxx_结束时间`, 'past', '30d')
-- ❌ 错误
WHERE RELATIVE_DATETIME_BETWEEN(`field_xxxxxx`, 'past', '30d')
```
### 13.2 树状/级联字段(如业务线)
先用 `workitem meta-fields` 获取 options:`--field-keys` 传 `["business"]`,或 `--field-query` 传 `业务线`;确认**叶子 option_id 与 label**。
| 语义 | 写法 |
|------|------|
| 父级(含所有下级) | `tree-multi-select`(数组型)用 `` any_match(`业务线`, x -> x = '<父级 label>') `` 或 `` `业务线` IN ('<父级 label>') ``;`tree-select`(单选)不支持父级级联,仅按叶子精确匹配 |
| 叶子等值 | `` `业务线` = '<叶子 label>' `` |
| ❌ 完整路径 | `` `业务线` = '<父级>/<叶子>' `` |
创建/更新类接口优先使用叶子节点 `option_id`。
---
## 14. Lambda 表达式限制
Lambda `x -> ...` 内部服务端**只受理下列极简条件**,其余一律拒回 `lambda predicate operator not supported: <OP>` 或 `unsupported lambda predicate`。
### 14.1 支持 / 不支持
| ✅ 支持 | ❌ 不支持 |
|--------|----------|
| `x = 'value'` | `x != 'a'` |
| `x IN ('a','b',...)`(值可含 `team(...)` / `current_login_user()` / `<id:userkey>`) | `x NOT IN (...)` |
| 同变量 OR:`x = 'a' OR x = 'b'`(推荐改 `IN`) | `x > / >= / < / <=` |
| — | `LIKE` / `NOT LIKE` |
| — | `BETWEEN` |
| — | `IS NULL` / `IS NOT NULL` |
| — | `RELATIVE_DATETIME_*(x,...)` |
| — | `AND` 复合条件(同/跨变量) |
| — | 嵌套 `any_match` / `none_match` |
### 14.2 决策规则
- 多选/数组字段优先直接用顶层 `IN` / `NOT IN` / `array_contains` / `NOT array_contains` / `none_match`
- **树状/级联字段父级匹配**(含所有下级):`tree-multi-select`(数组型)用 `any_match(field, x -> x = '<父级 label>')` 或 `` `field` IN ('<父级 label>') ``;`array_contains` 仅精确匹配、不含子级级联。`tree-select`(单选)不支持父级级联,仅按叶子 label / option_id 精确匹配
- 仅当 lambda 值列表需引用 `team(...)` / `current_login_user()` 等**返回集合的函数**,或需在集合内做多个 `<id:userkey>` 判断,才使用 `any_match(x -> x IN (...))`
- `any_match` 第二参数**必须**是 `x -> ...` 形式的 lambda;不接受数组字面量
---
## 15. 完整示例
> 示例中字段 key / 角色 / options 均以 `workitem meta-fields` / `workitem meta-roles` 返回为准;两者都必须同时传 `project_key` 与 `work_item_type`。
### 15.1 数组包含 + 当前用户 + 未归档
```sql
SELECT `work_item_id`, `name`, `work_item_status`, `priority`
FROM `project_key`.`story`
WHERE array_contains(`current_status_operator`, current_login_user())
AND `archiving_date` IS NULL
```
### 15.2 相对时间
```sql
SELECT `work_item_id`, `name`, `start_time`
FROM `project_key`.`story`
WHERE RELATIVE_DATETIME_BETWEEN(`start_time`, 'past', '30d')
```
### 15.3 逾期未完成(日期区间字段 + 状态过滤)
```sql
SELECT `work_item_id`, `name`, `work_item_status`
FROM `project_key`.`story`
WHERE RELATIVE_DATETIME_LT(`__field_xxxxxx_结束时间`, 'today')
AND `work_item_status` != '<完成态label>'
```
### 15.4 团队角色(`team list` 前置)
> 团队名 `'真实团队名'` 是占位。**必须先用 `team list` 拉取真实团队名,并传 `--project-key`**,否则报 `attribute_value not found`。
```sql
-- 主写法
SELECT `work_item_id`, `name`, `priority`
FROM `project_key`.`story`
WHERE any_match(`__后端开发`, x -> x IN (team(true, '真实团队名')))
-- fallback(仅角色名冲突时)
WHERE any_match(`__role_<project_key>_story_<role_id>`, x -> x IN (team(true, '真实团队名')))
```
### 15.5 综合(模糊 + 数组 + 排序分页)
```sql
SELECT `work_item_id`, `name`, `work_item_status`, `priority`
FROM `project_key`.`issue`
WHERE `name` LIKE '%性能优化%'
AND array_contains(`current_status_operator`, current_login_user())
AND `priority` = 'P0'
ORDER BY `updated_at` DESC
LIMIT 50
```
### 15.6 节点属性 + 延期标识
```sql
-- 所属节点当前负责人(单条件;`__BELONGING` 属性禁与其它 `__BELONGING` 属性 AND 组合,禁被 `array_contains` 包裹后与其它条件组合,见下方警示)
SELECT `work_item_id`, `name`, `work_item_status`
FROM `project_key`.`story`
WHERE array_contains(get_node_attribute('__BELONGING','当前负责人'), '<id:userkey>')
-- 所属节点状态:必须用 `=` 顶层比较(返回空 list 无 error),禁止 array_contains 包裹
SELECT `work_item_id`, `name`
FROM `project_key`.`story`
WHERE get_node_attribute('__BELONGING','状态') = '<进行中状态label>'
-- 开始节点已延期(risk_label() 等值是「集合完全相等」,用 JSON 数组字符串,属例外)
SELECT `work_item_id`, `name`
FROM `project_key`.`story`
WHERE risk_label() = '["延期/开始"]'
```
> ⚠️ **服务端已知限制(权威定义,其它章节引用本节)**:`get_node_attribute('__BELONGING','状态')` 一旦被 `array_contains` 包裹(单条件即触发),或任意 `__BELONGING` 属性之间做 AND 组合(如"当前负责人 + 状态"),会触发 nil pointer / panic。修复策略:状态类**必须**用 `=` 顶层比较;负责人等多值属性**必须**保持单条件;如需组合,改用 `__ALL` 属性或状态字段(`work_item_status`)+ `current_status_operator` 拼接。
### 15.7 关系查询(链式 + 跨空间字段)
```sql
-- 子任务的父工作项名称包含"登录"
SELECT `work_item_id`, `name`
FROM `project_key`.`sub_task`
WHERE any_relation_match(relation_field_chain('__父工作项'), x -> x.`name<target:all>` like '%登录%')
-- 多级关系:子任务→父工作项→关联软件
SELECT `work_item_id`, `name`
FROM `project_key`.`sub_task`
WHERE any_relation_match(relation_field_chain('__父工作项','需求关联软件'), x -> x.`name<target:all>` = '某软件')
```
### 15.8 状态时间 + 节点负责人
```sql
-- issue(状态流):状态窗口
SELECT `work_item_id`, `name`, `work_item_status`
FROM `project_key`.`issue`
WHERE status_time('<状态名>') between '2025-01-01' and '2025-12-31'
-- story(节点流):开始节点负责人
SELECT `work_item_id`, `name`
FROM `project_key`.`story`
WHERE array_contains(get_node_attribute('开始','负责人'), '<id:userkey>')
```
---
## 附:关键词 → 语法 映射
### 控件关键词
| 用户关键词 | 语法 |
|-----------|------|
| 参与人员、全部参与人员 | `all_participate_persons()` |
| 当前参与人 | `participate_persons()` |
| 流程节点、所有节点 | `all_nodes_name()` |
| 进行中节点 | `in_progress_nodes_name()` |
| 节点排期、节点估分、所属节点 | `get_node_attribute(node, attr)`(所属节点用 `__BELONGING`) |
| 节点延期标识 | `risk_label()` |
| 关联工作项字段 | `relation_field_chain('rel1',...)`(≤3 跳) |
| 子任务父工作项 | `relation_field_chain('__父工作项')` |
| 子任务来源 | `linked_work_item()` |
| 状态时间窗口(状态流) | `status_time('状态名') between ...` |
| 状态累计时长(状态流) | `status_time('__<状态名>_结束时间') - status_time('__<状态名>_开始时间')`(仅 SELECT) |
### 关系语境
| 关键词 | 函数 |
|-------|------|
| 每一个 | `all_relation_match` |
| 存在一个/一组 | `any_relation_match` |
| 每一个不满足 | `none_relation_match` |
| 存在一个不满足 | `not all_relation_match` |
### 数组语义
| 语义 | 语法 |
|------|------|
| 存在选项属于(OR) | `IN + 元组`(首选);控件函数用 `any_match` |
| 全部选项均不属于 | `NOT IN`(首选);`none_match` 备选 |
| 同时包含(AND) | `array_contains(field, 'a', 'b')` |
| 集合完全相等 | `` `field` = ('v1','v2') ``(禁 JSON 数组字符串,控件函数除外) |
references/performance.md›
# 性能与并发调用指南
本文件收录减少延迟的工程性规则。核心协议(字段格式、错误自愈)仍在 SKILL.md 主文件中。
## 并行调用
无依赖的命令调用应并行发起,有依赖则必须串行。
**必须串行**(前者输出是后者输入):
- `project search` → `workitem meta-fields` → `workitem query`
- `workitem get` → `workflow transition` / `workitem update`
- `workitem meta-fields` → `workitem create`
**可并行**:
- `workitem meta-fields` 和 `workitem meta-roles`(同类型)
- 多种工作项类型的 `workitem meta-fields`(如 story + issue)
- 各条件的 count 查询、多人排期分批查询
## 大结果处理
- **分批查询**:`workhour list-schedule` 多人时拆成每批 ≤ 20 人并行
- **精简 SELECT**:只选必要字段,避免富文本等大体积字段
- **按需翻页**:先读首页获取总数,按需翻页
references/rich-text-editor-markdown-syntax.md›
# 富文本编辑器 Markdown 格式规范
## 概述
富文本编辑器使用基于 **GFM(GitHub Flavored Markdown)** 的扩展 Markdown 格式。对于标准 Markdown 无法表达的功能,通过 HTML 注释和标签进行扩展。该格式可转换为 DSL、Delta 和 doc_html。
**核心原则:** 标准 GFM + HTML 注释承载元数据 + `<span>`/`<u>` 标签补充样式能力。
## 速查表
| 功能 | 语法 | 说明 |
|------|------|------|
| 加粗 | `**文本**` | |
| 斜体 | `*文本*` | |
| 删除线 | `~~文本~~` | |
| 下划线 | `<u>文本</u>` | HTML 标签,非标准 MD |
| 行内代码 | `` `代码` `` | |
| 字体颜色 | `<span style="color: rgb(R, G, B)">文本</span>` | 必须使用 `rgb()` 格式 |
| 背景颜色 | `<span style="background-color: rgb(R, G, B)">文本</span>` | 必须使用 `rgb()` 格式 |
| 字体大小 | `<span style="font-size: Npx">文本</span>` | 值为 px 单位 |
| 标题 | `#` 到 `######` | h1-h6 |
| 有序列表 | `1. 项目` | 嵌套用 4 空格缩进 |
| 无序列表 | `- 项目` | 嵌套用 4 空格缩进 |
| 任务列表 | `- [ ] 待办` / `- [x] 已完成` | |
| 引用块 | `> 文本` | 内部支持嵌套块级元素 |
| 代码块 | ` ```语言 ... ``` ` | 开头栅栏后跟语言标识 |
| 链接 | `[文本](url)` | |
| 图片 | `` | 写入时无需提供图片 UUID,服务会自动生成 |
| 链接预览 | `[文本](url)<!-- linkPreview -->` | 注释前无空格 |
| 分割线 | `---` | |
| 表情 | `:ShortCode:` | 大小写敏感的规范键名 |
| 居中对齐 | `<!-- center:start -->` ... `<!-- center:end -->` | 区域式 |
| 右对齐 | `<!-- right:start -->` ... `<!-- right:end -->` | 区域式 |
| 两端对齐 | `<!-- justify:start -->` ... `<!-- justify:end -->` | 区域式 |
| @提及 | `@名字<!-- mention:{JSON} -->` | 注释前无空格 |
## 扩展语法详解
### 对齐方式(区域式)
用 start/end 注释对包裹一个或多个段落,标签必须独占一行:
```markdown
<!-- center:start -->
这段文字居中显示。
这段也是居中的。
<!-- center:end -->
<!-- right:start -->
右对齐内容。
<!-- right:end -->
```
支持的值:`center`(居中)、`right`(右对齐)、`justify`(两端对齐)。左对齐为默认值,无需标记。
### @提及(带元数据)
为了在转换过程中保留用户身份信息,使用元数据格式。`@名字` 和注释之间**不能有空格**:
```markdown
@张三<!-- mention:{"id":"lark_user_id_7361251974161006596","cn_name":"张三","en_name":"Zhang San","email":"[email protected]","blockType":"AT_USER_BLOCK"} -->
```
注释中必填的 JSON 字段:
| 字段 | 说明 | 示例 |
|------|------|------|
| `id` | 用户 ID | `"lark_user_id_7361251974161006596"` |
| `cn_name` | 中文名 | `"张三"` |
| `en_name` | 英文名 | `"Zhang San"` |
| `email` | 邮箱地址 | `"[email protected]"` |
| `blockType` | 固定值 | `"AT_USER_BLOCK"` |
可选字段:`blockId`(UUID v4)、`type`(0 = 用户)、`avatar_url`。
同一行多个提及(之间不加空格):
```markdown
@张三<!-- mention:{"id":"id_1","cn_name":"张三","en_name":"Zhang San","email":"[email protected]","blockType":"AT_USER_BLOCK"} -->@李四<!-- mention:{"id":"id_2","cn_name":"李四","en_name":"Li Si","email":"[email protected]","blockType":"AT_USER_BLOCK"} -->
```
如果没有元数据,纯 `@名字` 也可接受,但无法在格式转换中完整还原。
### 图片
写入图片时只需使用标准 Markdown 图片语法,无需提供图片 UUID:
```markdown

```
图片 UUID 由服务自动生成。读取富文本内容时,图片后可能带有服务返回的 UUID 注释,例如:
```markdown
<!-- *****-*****-**** -->
```
该 UUID 是读取结果中的图片标识;写入者无需手动生成或追加。
### 链接预览
在链接后紧跟 `<!-- linkPreview -->`(**无空格**):
```markdown
[https://example.com/page](https://example.com/page)<!-- linkPreview -->
```
### 下划线
标准 Markdown 不支持下划线,使用 HTML `<u>` 标签:
```markdown
<u>带下划线的文本</u>
```
可与其他格式嵌套:
```markdown
*<u>斜体加下划线</u>*
**<u>加粗加下划线</u>**
```
### Span 样式
字体颜色、背景颜色、字体大小使用 `<span>` 的 `style` 属性。**颜色必须用 `rgb(R, G, B)` 格式**(不支持 hex 和颜色名):
```markdown
<span style="color: rgb(245, 74, 69)">红色文字</span>
<span style="background-color: rgb(53, 189, 75)">绿色背景</span>
<span style="font-size: 18px">大号文字</span>
```
### 表情短代码
使用 `:CODE:` 格式。键名大小写敏感,解析时会归一化到 lark 规范形式:
```
:OK: :DarkThumbsup: :THANKS: :DarkFightOn: :DarkFingerHeart: :APPLAUSE: :LightFistBump: :JIAYI: :DONE: :SMILE: :Delighted: :BeamingFace: :BLUSH: :LAUGH: :SMIRK: :LOL: :FACEPALM: :LOVE: :ERROR: :CRY: :SOB: :THINKING: :SCOWL: :SMART: :WITTY: :PROUD: :WINK: :NOSEPICK: :HAUGHTY: :SLAP: :SPITBLOOD: :TOASTED: :ColdSweat: :BLACKFACE: :FullMoonFace: :GLANCE: :DULL: :ROSE: :HEART: :PARTY: :INNOCENTSMILE: :SHY: :CHUCKLE: :JOYFUL: :WOW: :OBSESSED: :DROOL: :SMOOCH: :KISS: :EMBARRASSED: :TEARS: :ENOUGH: :YEAH: :TRICK: :MONEY: :TEASE: :SHOWOFF: :COMFORT: :CLAP: :PRAISE: :STRIVE: :XBLUSH: :SILENT: :HUG: :WHIMPER: :CRAZY: :WAIL: :LOOKDOWN: :DIZZY: :FROWN: :WHAT: :WAVE: :BLUBBER: :WRONGED: :HUSKY: :SHHH: :SMUG: :ANGRY: :HAMMER: :SHOCKED: :TERROR: :PUKE: :SICK: :YAWN: :DROWSY: :SLEEP: :SPEECHLESS: :SWEAT: :SKULL: :PETRIFIED: :BETRAYED: :HEADSET: :EatingFood: :Typing: :Lemon: :Get: :LGTM: :OnIt: :OneSecond: :YouAreTheBest: :Shrug: :ThanksFace: :SaluteFace: :GoGoGo: :Partying: :VRHeadset: :MeMeMe: :Sigh: :DarkSalute: :DarkShake: :LightHighFive: :DarkWavingHand: :DarkClick: :DarkThumbsDown: :ClownFace: :SLIGHT: :TONGUE: :LIPS: :SiSiASYouWish: :HappyDragon: :JubilantRabbit: :RoarForYou: :CALF: :BULL: :BEAR: :EYESCLOSED: :BEER: :CAKE: :GIFT: :CUCUMBER: :Drumstick: :Pepper: :CANDIEDHAWS: :BubbleTea: :Coffee: :Pin: :AWESOMEN: :Hundred: :MinusOne: :CrossMark: :CheckMark: :OKR: :No: :Yes: :Alarm: :Loudspeaker: :Trophy: :Fire: :RAINBOWPUKE: :Music: :TV: :Movie: :Pumpkin: :LUCK: :FORTUNE: :REDPACKET: :BeAtTheForefront: :2026: :FIREWORKS: :XmasHat: :Snowman: :XmasTree: :FIRECRACKER: :StickyRiceBalls: :Mooncake: :MoonRabbit: :HEARTBROKEN: :BOMB: :POOP: :18X: :CLEAVER: :GeneralWorkFromHome: :GeneralBusinessTrip: :StatusFlashOfInspiration: :StatusReading: :GeneralInMeetingBusy: :Status_PrivateMessage: :GeneralDoNotDisturb: :Basketball: :Soccer: :StatusEnjoyLife: :GeneralTravellingCar: :StatusBus: :StatusInFlight: :GeneralSun: :GeneralMoonRest:
```
解析器会归一化大小写(`:smile:` → `:SMILE:`,`:beamingface:` → `:BeamingFace:`),但建议直接使用规范写法。
### 列表 — 4 空格缩进
嵌套列表使用 **4 个空格**(不是 2 个)缩进:
```markdown
1. 第一级有序
1. 第二级(4 空格)
1. 第三级(8 空格)
- 混合:有序中嵌套无序(4 空格)
- 第一级无序
- 第二级
- 第三级
1. 混合:无序中嵌套有序
```
任务列表:
```markdown
- [ ] 未完成任务
- [x] 已完成任务
- [ ] 嵌套未完成
- [x] 嵌套已完成
```
### 引用块
支持嵌套和内部块级内容:
```markdown
> 带 **加粗** 和 *斜体* 的引用
> 1. 引用内有序列表
> 2. 第二项
> 1. 引用内嵌套列表
> - 引用内无序列表
```
### 代码块
使用围栏式代码块,开头标注语言:
````markdown
```TypeScript
function add(a: number, b: number): number {
return a + b;
}
```
```Go
func add(a, b int) int {
return a + b
}
```
````
### GFM 表格(简单内容)
表格内仅包含行内内容(文本、加粗、链接等)时使用:
```markdown
| 表头1 | 表头2 | 表头3 |
|-------|-------|-------|
| 单元格1 | **加粗** | [链接](url) |
| 单元格3 | 单元格4 | 单元格5 |
```
### HTML 表格(单元格内含富内容)
当表格单元格需要块级元素(标题、列表、代码块、对齐、图片)时,使用 HTML `<table>` 语法。**`<td>` 后和 `</td>` 前必须留空行**,这样内部的 Markdown 才能被正确解析:
```markdown
<table>
<tr>
<td>
# 单元格内标题
**加粗段落**
</td>
<td>
1. 有序列表
2. 在单元格中
- 嵌套项
</td>
<td>
<!-- center:start -->
单元格内居中
<!-- center:end -->
</td>
</tr>
<tr>
<td>
```TypeScript
// 单元格内代码块
const x = 1;
```
</td>
<td>
> 单元格内引用块
</td>
<td>
<span style="color: rgb(245, 74, 69)">单元格内彩色文字</span>
</td>
</tr>
</table>
```
空单元格:`<td></td>`
## 格式组合
行内样式可以嵌套使用:
```markdown
**~~加粗删除线~~**
*<u>斜体下划线</u>*
[**加粗链接**](https://example.com)
<span style="color: rgb(245, 74, 69)">**红色加粗**</span>
```
## 段落分隔
每个块级元素(段落、标题、列表组、表格、代码块、引用块)之间用空行分隔:
```markdown
# 标题
第一段正文。
第二段正文。
- 列表项 1
- 列表项 2
列表后面的段落。
```
## 常见错误
| 错误写法 | 正确写法 |
|----------|----------|
| `<b>加粗</b>` | `**加粗**` |
| `<i>斜体</i>` | `*斜体*` |
| `<s>删除</s>` 或 `<del>删除</del>` | `~~删除~~` |
| `<span style="color: #ff0000">` | `<span style="color: rgb(255, 0, 0)">` |
| `<span style="color: red">` | `<span style="color: rgb(255, 0, 0)">` |
| `<!-- center -->文本<!-- /center -->` | `<!-- center:start -->\n文本\n<!-- center:end -->` |
| `@名字 <!-- mention:... -->`(有空格) | `@名字<!-- mention:... -->`(无空格) |
| `[链接](url) <!-- linkPreview -->`(有空格) | `[链接](url)<!-- linkPreview -->`(无空格) |
| 2 空格嵌套列表缩进 | 4 空格嵌套列表缩进 |
| `:smile:`(全小写) | `:SMILE:`(使用规范大小写) |
| `<!-- 图片uuid -->`(手动填写 UUID) | ``(写入时由服务自动生成 UUID) |
| `<!-- linkPreview -->` | `<!-- linkPreview -->` 仅用于 `[文本](url)` 链接 |
| `<td>文本</td>`(`<td>` 后无空行) | `<td>\n\n文本\n\n</td>`(需要空行) |
## 完整示例
```markdown
# 项目进展
## 状态
<!-- center:start -->
**Alpha 项目 — 迭代评审**
<!-- center:end -->
本迭代完成了以下工作:
1. 用户认证
1. 登录流程
2. 密码重置
2. 仪表盘改版
- 新布局
- 性能优化
### 关键指标
| 指标 | 改版前 | 改版后 |
|------|--------|--------|
| 加载耗时 | 3.2s | **1.1s** |
| 错误率 | 2.4% | <span style="color: rgb(53, 189, 75)">0.3%</span> |
### 代码变更
```TypeScript
export function authenticate(token: string): boolean {
return validateJWT(token);
}
```
> 注意:部署前需要更新 <u>环境配置</u>。
- [x] 代码评审已完成
- [x] 测试通过
- [ ] 部署到预发环境
负责人:@张三<!-- mention:{"id":"lark_user_id_001","cn_name":"张三","en_name":"Zhang San","email":"[email protected]","blockType":"AT_USER_BLOCK"} -->@李四<!-- mention:{"id":"lark_user_id_002","cn_name":"李四","en_name":"Li Si","email":"[email protected]","blockType":"AT_USER_BLOCK"} -->
参考文档:[迭代看板](https://example.com/sprint/42)<!-- linkPreview -->
:DONE: :DarkThumbsup:
```
references/sop-create-workitem.md›
# 创建工作项
> **CRITICAL** — 开始前 MUST 先用 Read 工具读取 `../SKILL.md`,其中包含前置检查、授权流程、命令参数参考、字段值格式、通用规范和错误处理。
本技能用于在飞书项目中创建工作项(需求、任务、缺陷等),全程自动化执行。
---
## 执行流程
### STEP 1 — 提取意图
从用户输入中提取:
- **空间名**:哪个项目空间
- **工作项类型**:需求 / 任务 / 缺陷 / 其他
- **字段值**:标题、优先级、负责人、描述等
- **URL**(如有):先调 `url decode` 解析。`url_kind` 非 `workitem_create` / `workitem_detail` 时按 [url-kinds.md](url-kinds.md) 拒绝或追问;命中则用返回的 `simple_name` 取代空间名探测,`work_item_type` 取代类型探测。**禁止**自己从 URL 截取路径段作参数。
### STEP 2 — 确认空间和类型
1. 用 `project search` 验证空间 → 获取 `project_key`
2. 用 `workitem meta-types` 获取类型列表 → 确认 `work_item_type`
> 唯一匹配则直接用,多个匹配则展示列表让用户选,无匹配则问用户。**禁止猜测。**
### STEP 3 — 收集元数据(并行)
同时发起以下调用:
| 调用 | 目的 |
|------|------|
| `workitem meta-create-fields` | 获取该类型的**必填字段**元信息(前置校验,防止因空间自定义必填项缺失导致创建失败) |
| `workitem meta-fields(field_keys=["template"])` | **获取模板 ID**(创建必填) |
| `workitem meta-fields(field_query="用户提到的字段名")` | 确认字段 key、类型、枚举值(通过 `field_keys` 精确匹配或 `field_query` 模糊搜索,避免全量拉取时因分页导致自定义字段遗漏) |
| `workitem meta-roles(page_num=1)` | 获取角色定义(如用户指定了负责人等角色) |
如用户提到了人名,并行调用 `user search` 转换为 userkey。
> 默认只查询在职用户。只有当用户明确要求包含离职、停用等非在职人员,或在职结果为空且用户要求继续查找时,才给 `user search` 传 `--need-all-status=true`。
### STEP 4 — 自动匹配模板
根据 STEP 3 获取的模板枚举值:
- **只有一个模板** → 自动选择
- **多个模板** → 根据用户描述中的关键词匹配最接近的模板名,选不出来时展示列表让用户选
- **用户明确指定了模板名** → 精确匹配
### STEP 5 — 自动补全必填项与转换字段值
**1. 自动补全必填项**
严格对比 `workitem meta-create-fields` 返回的必填字段与用户已给定的字段。对于缺失的必填项(尤其是空间自定义的特殊必填项),自动生成合理的默认值:
- 枚举:默认取第一项
- 文本:填"自动生成"
- 布尔:填 true
**2. 转换字段值**
将用户给的自然语言值及自动生成的默认值转换成 API 需要的格式:
| 来源 | 转换 |
|------|------|
| 人名 | 调用 `user search` 批量转换为 userkey |
| 枚举值 | 从字段配置的 options 中按 option_name 匹配得到 option_id |
| 日期 | 转为毫秒时间戳 |
| 其他类型 | 按主文档 [SKILL.md](../SKILL.md)「字段值格式」规范转换 |
**格式速查**(创建场景特有约束;通用类型见主文档 [SKILL.md](../SKILL.md)「字段值格式」章节):
> 🚨 **关键约定**:`field_value` 协议层是 **STRING**。标量直接字符串化;数组/对象**必须先 JSON.stringify**,否则会报 `need STRING type, but got: LIST`。
| 字段类型 | 差异化提示 |
|---------|-----------|
| `role_owners` | **仅创建时可用**;stringified 对象数组 `"[{\"role\":\"<role_id>\",\"owners\":[\"<userkey>\"]}]"` |
| `signal` | option_id 纯字符串 `"<option_id>"`(以 `workitem meta-fields` 的 `options[].option_id` 为准;不接受 `"true"`/`"false"`/`"null"`) |
| `workitem_related_multi_select` | **stringified** ID 数组,**禁止写入自身 ID**(防循环引用,触发 `exists loop`) |
| `file` / `multi-file` | 先调 `attachment +upload`,传 `--resource-type=15`、`--project-key`、`--work-item-type`、`--field-key` 和本地文件路径(工作项尚未创建,不传 `--work-item-id`)拿 `file_token`,再 stringify 数组 `"[{\"name\":\"a.pdf\",\"type\":\"application/pdf\",\"size\":\"12345\",\"fileToken\":\"<token>\"}]"` |
> 其余通用字段类型(text / user / multi-user / date / schedule / precise_date / select 系列等)写入格式详见主文档 [SKILL.md](../SKILL.md)「字段值格式」章节。
**角色设置**(创建时):通过 fields 中的 `role_owners` 字段,值为 stringified 对象数组:
```json
{"field_key":"role_owners","field_value":"[{\"role\":\"<role_id>\",\"owners\":[\"<userkey>\"]}]"}
```
> **角色补充**:创建时除了能在 `fields` 数组内处理极少部分内置的 role_owners 字段外,针对其他自定义角色(如 PO、PM、Tech Lead 等),须在创建后用 `workitem update` 的 `role_operate` 参数追加写入。
### STEP 6 — 创建
```bash
meegle workitem create --work-item-type 类型key --fields '[{"field_key":"template","field_value":"模板ID"},{"field_key":"name","field_value":"标题"}]' --project-key 空间key --format json
```
🚨 **批量创建**:当用户要求批量创建多个工作项时,必须**串行调用**(逐个请求),禁止高并发,以免触发平台限流。每个 field_value 均须符合「字段值格式」的 STRING 约定(标量直接字符串化;数组/对象 JSON.stringify)。
### STEP 7 — 确认结果
创建成功后,向用户展示:
- 工作项 ID 和名称
- 链接(如返回中包含)
- 已设置的关键字段摘要
---
## 特殊字段写入规则
### 级联选项 (Tree-select)
1. **格式极简**:对于业务线(`_business`)等级联单选字段,`field_value` **只传 `option_id` 纯字符串**,不要传 `value/label/children` 的复杂 JSON。
2. **层级强校验**:如果报错 `级联选项字段值不满足层级配置`,说明该字段要求选到末级叶子节点。此时查询该选项的 `children` 树,**将末级叶子节点列表展示给用户选择**,禁止自行向下盲猜。
### 不可写入的字段类型
以下字段类型不支持通过 API 写入,遇到时**直接跳过并告知用户原因**:
| 类型 | 原因 |
|------|------|
| `vote-boolean`(轻量表态) | 计数器,只能由用户在界面操作 |
| `vote-option` / `vote-option-multi`(投票) | 不支持通过接口伪造投票结果 |
| 计算字段 | 系统自动计算,只读 |
### 富文本与关联字段
- **富文本/多行文本**:直接传 Markdown 字符串(`# 标题`、`|列1|列2|`、代码块等)。
- **关联云文档(PRD)**:传 URL 数组,如 `["https://xxx.feishu.cn/docx/xxx"]`。
- **前置依赖/关联工作项**:传目标工作项 ID。**注意**:不同空间可能要求字符串 `"<work_item_id>"` 或数字格式,遇到类型校验失败立刻切换格式重试。用户提供的是工作项**名称而非 ID** 时,按 [field-value-extras.md](field-value-extras.md)「关联工作项名称 → ID 转换」完整流程处理。
- **系统外信号类型 (`signal`)**:传 `option_id` 纯字符串(如 `"<option_id>"`;以 `workitem meta-fields` 的 `options[].option_id` 为准)。**不接受 `"true"` / `"false"` / `"null"`**。当前系统外信号常见 4 个 option:`passed` / `notpassed` / `processing` / `noinformationyet`。
---
## 错误自动恢复(自愈机制)
> 通用自愈规则(格式错误、级联层级、枚举不合法)见 [error-handling.md](error-handling.md)。以下为本 SOP 补充规则:
| 报错特征 | 自愈动作 |
|---------|---------|
| `json: unsupported type` / 网络超时 | 原参数直接重试 |
| 字段 key 不匹配 | 用 `field_query` 模糊搜索取最佳匹配 |
| 人名解析失败 | 尝试用邮箱前缀再搜一次 |
| 明确缺少必填字段 | 核对字段类型限制,关联工作项尝试数字↔字符串切换 |
---
## 熔断机制 (Circuit Breaker)
> 通用熔断规则(空间未找到、权限不足)见主文档 [SKILL.md](../SKILL.md)「错误处理」。以下为本 SOP 补充规则:
1. **工作项类型未找到**:`workitem meta-types` 失败超过 3 次
2. **字段转换大面积失败**:字段值转换失败比例 > 60%,终止流程并列出失败字段明细,**不要强行创建残缺数据**
---
## 常见问题
| 问题 | 处理 |
|------|------|
| 用户未指定空间 | 问用户 |
| 用户未指定类型 | 如空间只有一种类型则直接用,否则问用户 |
| 用户提到的字段不存在 | `workitem meta-fields(field_query="关键词")` 模糊查询,找不到则告知用户 |
| 模板有多个 | 根据关键词匹配,匹配不到则展示列表让用户选 |
| 枚举值匹配不到 | 展示该字段所有枚举值让用户选 |
| 人名匹配到多人 | 展示完整列表让用户指定,**禁止自行选择** |
references/sop-transition-node.md›
# 流转节点
> **CRITICAL** — 开始前 MUST 先用 Read 工具读取 `../SKILL.md`,其中包含前置检查、授权流程、命令参数参考、字段值格式、通用规范和错误处理。
本技能用于在飞书项目中流转节点流工作项的节点(confirm/rollback),全程自动化执行。
> **注意**:此技能仅用于**节点流**工作项(如需求),不适用于状态流工作项(如缺陷)。状态流转参见主文档 [SKILL.md](../SKILL.md) 的 `workflow transition-state`。
---
## 核心设计原则:最小查询 + 按需补充
`workflow transition` 工具**只接受 node_key(节点 ID),不支持传节点名称**。因此必须先通过 `workflow get-node` 获取名称→node_key 映射。但查询应保持精准轻量:
- **用户指定了节点名** → `node_id_list` 直接传中文名称 `["节点名"]`,精准查单个节点
- **用户说"所有节点"** → 传 `["_all"]` 查全量,但**不传 `field_key_list`**(不查表单字段)
- **直接尝试流转**:拿到 node_key 后立即调 `workflow transition`
- **按需补充字段**:仅当流转失败(提示必填字段未填)时,才查询必填字段并补充
> **为什么不预查全量字段?** `workflow get-node` 有分页限制(每页 20 个节点),查全量字段极慢。大多数场景无需补充字段,只在流转失败时按需查目标节点的必填字段即可。
---
## 执行流程
### STEP 1 — 定位工作项
从用户输入中提取 work_item_id 和 project_key:
- 用户给了 **URL** → 先调 `url decode`。只有 `url_kind == workitem_detail` 才能进入本 SOP;其他 kind 按 [url-kinds.md](url-kinds.md) 拒绝或追问
- 用户给了 **ID** → 需同时确定 project_key
- 信息不足时才追问
> **URL 处理**:decode 返回的 `simple_name` 必须再调 `project search` 转为权威 `project_key`(同名空间可能有多个无权限)。**禁止**自己从 URL 截取路径段作参数。`work_item_id` 参数必须是字符串类型。
### STEP 2 — 精准查节点
根据用户意图选择最高效的查询方式:
| 用户意图 | `node_id_list` 传参 | `field_key_list` | 说明 |
|---|---|---|---|
| 指定了节点名(如"完成开发中") | `["开发中"]`(直接传中文名) | 不传 | 精准查单个节点 |
| 指定了多个节点名 | `["开发中", "测试中"]` | 不传 | 精准查指定节点 |
| "当前节点" / 未指定节点 | `["_all"]` | 不传 | 查全量找 status="doing" |
| "所有节点" / "全部流转" | `["_all"]` | 不传 | 查全量但不带字段 |
从返回结果中获取 **name**、**node_key**、**status**(finished/doing/not_started)。
**自动确定操作类型:**
- "完成"、"流转"、"确认"、"推进" → action = `confirm`
- "回滚"、"退回"、"撤回" → action = `rollback`
- 未明确说 → 默认 `confirm`
**目标节点确定:**
- 用户指定了节点名 → 从返回结果中取 node_key
- 用户说"当前节点" → 选 status = "doing" 的节点
- 用户说"所有节点" → 按顺序逐个处理未完成节点
- 用户没指定 → 自动选当前进行中的第一个节点
- 名称匹配不到 → 用 `["_all"]` 重新查全量,列出所有节点供用户选择
**回滚操作**:从用户输入提取原因;用户没给则用"用户发起回滚"作默认原因。
### STEP 3 — 直接尝试流转
```bash
meegle workflow transition --work-item-id 工作项ID --project-key 空间key --node-id 节点node_key --action confirm --rollback-reason '{{rollback_reason}}' --format json
```
**三种结果分支:**
| 结果 | 处理 |
|---|---|
| 流转成功 | 直接跳到 STEP 6 返回结果 |
| 必填字段未填写 | 进入 STEP 4 补充字段 |
| 其他错误(权限/节点不存在等) | 进入错误恢复逻辑 |
### STEP 4 — 按需补充必填字段(仅流转失败时)
只有当 STEP 3 流转失败、提示必填字段未填时才进入本步骤。
**4.1 查询未完成的必填字段**
```bash
meegle workflow list-state-required --work-item-id 工作项ID --state-key 目标节点node_key --project-key 空间key --mode {{mode}} --format json
```
传入 `mode = "unfinished"` 仅查未完成必填项。从返回中识别每个字段的 `form_item_type`(node_field / field)和 `field_type`。
**4.2 评审结论 / 评审意见(`node_finished_conclusion` / `node_finished_opinion`)**
这是节点的「整体完成结论 / 意见」,可经接口读写,但**前提是该节点已启用这两个字段**——只有节点开了「完成结论」配置,`workflow get-node` 的 `form_items` 里才会出现它们;没启用时写入会报 `node field is invalid`。所以**先读 `form_items` 确认字段存在,再写**。
**读取**:用 `workflow get-node` 按 `field_key_list` 读,或用 `workitem get` 按 `fields` 读。字段未启用时返回里不会出现对应项:
```bash
meegle workflow get-node --work-item-id 工作项ID --field-key-list '["node_finished_conclusion","node_finished_opinion"]' --need-sub-task {{need_sub_task}} --page-num {{page_num}} --project-key 空间key --node-id-list '["节点node_key"]' --format json
```
**查询结论选项**:结论是 select 型,用 `workflow meta-node-fields` 查 options,写入用其 `option_id`:
```bash
meegle workflow meta-node-fields --field-keys '["node_finished_conclusion"]' --field-types '{{field_types}}' --project-key 空间key --work-item-type 类型key --query '{{query}}' --format json
```
**写入**:经 `workflow update-node` 的 `fields` 写入——结论写合法 `option_id`,意见写文本。
> 🚨 **必须分两次调用,一次只写一个字段。** `workflow update-node` 的 `fields` 数组里如果同时放结论和意见,**只有第一个字段会落库,其余被静默丢弃**(返回仍是 `success`)。这和「排期 / 负责人不要同时改」是同一类约束。每次写完都要 `workflow get-node` 回读校验,不要凭返回的 success 断言已写入。
```bash
meegle workflow update-node --work-item-id 工作项ID --node-schedule '{{node_schedule}}' --schedules '{{schedules}}' --fields '[{"field_key":"node_finished_conclusion","field_value":"option_id"}]' --project-key 空间key --node-id 节点node_key --node-owners '{{node_owners}}' --format json
```
```bash
meegle workflow update-node --work-item-id 工作项ID --node-schedule '{{node_schedule}}' --schedules '{{schedules}}' --fields '[{"field_key":"node_finished_opinion","field_value":"评审意见文本"}]' --project-key 空间key --node-id 节点node_key --node-owners '{{node_owners}}' --format json
```
**4.3 硬拦截:不可写入的字段类型**
以下字段类型 **API 无法写入**。如果被设为流转必填项,**立即中断当前节点的流转**并告知用户:
| 字段类型 | 说明 | 拦截原因 |
|---|---|---|
| `actual_work_time` | 实际工时 | 需在页面手动登记 |
| `owners_finished_info` | 负责人完成结论与意见 | 仅各负责人可在页面操作 |
| `vote-boolean` / `vote-option` / `vote-option-multi` | 投票类 | 仅支持页面交互 |
| 计算字段 | 系统自动计算 | 只读 |
🚨 遇到硬拦截时输出:
> "节点流转失败。当前节点【节点名称】设置了必须填写【字段名称】(类型:xxx)才能流转。由于该字段类型不支持自动化补充,请您在飞书项目页面手动填写后,再通知我继续流转。"
**4.4 可补充字段的值转换**
**人员字段处理规则(极重要):**
- 用户明确指定了人员 → `user search` 转 userkey
- 搜索到多个同名用户 → 若用户说"分配给我自己"用 `current_login_user()`,否则**必须向用户确认**
- 用户未指定但为必填 → **向用户询问**,不要自动默认为当前用户
- 唯一例外:用户明确说"我来负责"/"分配给我"时才用 `current_login_user()`
**节点专属字段**(使用 `workflow update-node` 的专用参数):
| field_key | 更新方式 |
|---|---|
| `owner` (multi-user) | `node_owners` 参数。用户指定人 → search 转 userkey;用户说"我来" → current_login_user();**未指定 → 询问** |
| `schedule` | `node_schedule` 参数,格式 `{"estimate_start_date": ms, "estimate_end_date": ms, "owners": [userkey], "points": 数字}` |
| `point` (number) | `node_schedule` 中的 `points` 字段 |
> 清空节点负责人时传空数组 `[]`(不是 `["_all"]`,`["_all"]` 仅用于 `update_field` 中删除角色配置)。
> 评审结论 / 意见(`node_finished_conclusion` / `node_finished_opinion`)的读写口径见 §4.2:需节点已启用该字段,且**结论与意见必须分两次 `update-node` 调用**(一次只落第一个字段)。
**通用字段类型转换**(完整格式见主文档 [SKILL.md](../SKILL.md)「字段值格式」):
> 🚨 **关键约定**:表单字段 `field_value` 协议层是 **STRING**。标量直接传字符串;数组/对象**必须 JSON.stringify**,否则报 `need STRING type, but got: LIST`。(上方「节点专属字段」走 `workflow update-node` 的专用参数,不受此约定影响。)
| field_type | 节点流转场景差异化提示 |
|---|---|
| `schedule`(表单字段) | **stringified** `"[开始ms,结束ms]"`;**节点专属排期不走本表**,用 `workflow update-node` 的 `node_schedule` 参数 |
| `signal` | `option_id` 字符串(以 `workitem meta-fields` 的 `options[].option_id` 为准;不接受 `"true"`/`"false"`/`"null"`) |
| `workitem_related_multi_select` | **stringified** ID 数组,**禁止写入自身 ID**(防循环引用,触发 `exists loop` 报错) |
| `tree-select` | 只传 `"option_id"` 纯字符串,不传 value/label/children 复杂 JSON |
| `file` / `multi-file` | 先调 `attachment +upload`,传 `--resource-type=15`、`--project-key`、`--work-item-id`、`--field-key` 和本地文件路径拿 `file_token`,再 **stringify** 数组 `"[{\"name\":\"a.pdf\",\"type\":\"application/pdf\",\"size\":\"12345\",\"fileToken\":\"<token>\"}]"` |
> 其余通用字段类型(text / number / bool / user / multi-user / date / precise_date / select 系列 / multi-text / telephone / email / workitem_related_select 等)写入格式详见主文档 [SKILL.md](../SKILL.md)「字段值格式」章节。
> **用户提供的是工作项名称而非 ID** 时,按 [field-value-extras.md](field-value-extras.md)「关联工作项名称 → ID 转换」完整流程(获取目标约束 → `workitem query` 搜索 → 消歧 → 按类型写入)处理。
**节点字段 vs 工作项字段**:
- `form_item_type = "node_field"` → `workflow update-node`,传 `node_id` + `fields`
- `form_item_type = "field"` → `workitem update`(工作项级别)
- 节点枚举值用 `workflow meta-node-fields` 查询;工作项枚举值用 `workitem meta-fields` 查询(用 `field_keys` 精确或 `field_query` 模糊搜索,禁止逐页遍历)
**4.5 字段补充执行策略**
🚨 **效率要求**:并行完成所有必填字段补充,禁止逐个串行。
1. **分类**:节点负责人(owner)→ `node_owners`;排期/估分 → `node_schedule`;其他字段 → `fields` 或 `workitem update`
2. **节点负责人和排期/估分不可同时更新**,需分两次调用 `workflow update-node`
3. 其他节点字段通过 `workflow update-node` 的 `fields` 参数批量更新
4. 工作项字段通过 `workitem update` 的 `fields` 参数批量更新
**4.6 用户未提供值时**:
- 人员类 → **必须询问**
- 排期/日期类 → **询问用户**
- 枚举类 → 列出选项让用户选
- 文本/数字/布尔 → 可给合理默认值(bool 默认 false,估分默认 1)
- 待确认字段 > 3 个时,一次性列出让用户批量回复
### STEP 5 — 补充后再次流转
字段补充完成后,**再次调用 `workflow transition`** 执行流转。仍然失败则读取错误信息重新处理(最多重试 2 次)。
### STEP 6 — 返回结果
展示表格汇总:
| 节点名称 | 操作 | 结果 | 备注 |
|---|---|---|---|
| 需求评审 | confirm | ✅ 成功 | — |
| 开发中 | confirm | ✅ 成功 | 自动补充了排期、估分、负责人 |
| 测试中 | confirm | ❌ 阻塞 | 必填字段「实际工时」不支持 API 更新 |
如果有阻塞节点,明确列出需要用户手动操作的字段和原因。
---
## 批量流转
当用户说"所有节点"/"全部流转"时:
1. 按节点顺序依次流转:直接调 `workflow transition` → 失败则按需补充 → 下一个
2. 每个节点独立处理,某个节点被阻塞不影响已完成的节点
3. 最终汇总所有节点的流转结果
---
## 错误自动恢复(自愈机制)
> 通用自愈规则(格式错误、级联层级、枚举不合法)见 [error-handling.md](error-handling.md)。以下为本 SOP 补充规则:
| 报错特征 | 自愈动作 |
|---------|---------|
| 节点名匹配不到 | 用 `["_all"]` 查全量节点,模糊匹配;仍失败则列出所有节点供用户选择 |
| 必填字段缺失 | 进入 STEP 4 按需补充 |
---
## 熔断机制 (Circuit Breaker)
> 通用熔断规则(空间未找到、权限不足)见主文档 [SKILL.md](../SKILL.md)「错误处理」。以下为本 SOP 补充规则:
1. **必填字段全部为硬拦截类型**:当前节点所有未完成必填字段都属于不可写类型
2. **连续流转失败**:同一节点重试 2 次仍然失败
---
## 常见问题
| 问题 | 处理 |
|------|------|
| 用户未指定工作项 | 追问工作项 ID 或 URL |
| 用户未指定节点 | 自动选 status="doing" 的当前节点 |
| 用户操作的是状态流工作项 | 提示改用 `workflow transition-state`,本技能仅处理节点流 |
| 流转报"必填字段未填" | 进入 STEP 4 按需补充 |
| 补充字段后仍失败 | 检查是否存在硬拦截字段,明确告知用户 |
references/sop-transition-state.md›
# 流转工作项状态(状态流)
> **CRITICAL** — 开始前 MUST 先用 Read 工具读取 `../SKILL.md`,其中包含前置检查、授权流程、命令参数参考、字段值格式、通用规范和错误处理。
本 Skill 用于**状态流工作项**(如缺陷 / issue)的状态流转,全程自动化编排。
> ⚠️ **仅限状态流**。需求 / story 等节点流工作项须改用 `workflow transition`(action=confirm/rollback),不要混用本 Skill。
---
## 执行流程
### STEP 1 — 定位工作项 + 获取当前用户(并行)
**并行执行**:
1. **定位工作项**:从用户输入中提取 `work_item_id`、`project_key`、`work_item_type`。
- **URL 解析**:用户给了链接则先调 `url decode`。只有 `url_kind == workitem_detail` 才能进入本 SOP;其他 kind 按 [url-kinds.md](url-kinds.md) 拒绝或追问。decode 返回的 `simple_name` 必须再调 `project search` 转为权威 `project_key`(同名空间可能有多个无权限)。**禁止**自己从 URL 截取路径段作参数。
- **ID 类型**:传给任何工具的 `work_item_id` 必须是 **字符串(String)**。
- 信息不足才追问。
2. **获取当前用户**:调用 `user me`,从返回体取 `user_key`(下一步必填)。`current_login_user()` 仅为 MQL 内置函数字面量,`user search` 不会解析该字符串。
### STEP 2 — 查询可流转状态并匹配目标
```bash
meegle workflow list-state-transitions --work-item-id 工作项ID --work-item-type 类型key --user-key 当前用户userkey --project-key 空间key --format json
```
- **匹配目标状态**:精确 / 模糊 / 语义匹配用户意图(如"关闭" → "已关闭","解决" → "已解决")拿到对应的 `transition_id`。
- 🚨 **未明确目标状态且有多个候选时**:**必须展示所有可选项让用户选择**,不得替用户默认选择。
### STEP 3 — Fail-fast 直接尝试流转
**不要前置查询必填项**,直接调用 `workflow transition-state`:
```bash
meegle workflow transition-state --work-item-id 工作项ID --project-key 空间key --transition-id 上一步拿到的ID --format json
```
- **流转成功** → 跳到 STEP 6 返回结果。
- **失败且提示必填字段未填** → 进入 STEP 4 按需补充。
- **失败其他报错** → 参考下方「智能修复」章节。
### STEP 4 — 按需收集并补充必填字段(仅流转失败时触发)
调用 `workflow list-state-required` 获取目标状态所需必填项:
```bash
meegle workflow list-state-required --work-item-id 工作项ID --state-key 目标状态key --project-key 空间key --mode {{mode}} --format json
```
> 若工具支持 `mode` 参数(如 `mode="unfinished"`),优先只查尚未填写的字段,减少噪音。
**4.1 硬拦截:不支持 API 更新的字段类型**
遇到以下字段被设为必填,**立即中断流转**并提示用户在页面手动填写:
- `vote-boolean` / `vote-option` / `vote-option-multi`(投票类)
- 计算字段(系统自动计算,只读)
> **复合明细表可先通过 `workitem update` 补充**,但 `compound_field` 与 `multi_user_compound_field` 的写协议不同;多人复合字段还是整体覆盖,必须先读取并保留全部人员和非目标数据。格式详见主文档 [SKILL.md](../SKILL.md)「字段值格式 → 复合明细表」章节;无法自动判断子字段值或人员范围时仍需询问用户。
> 中断话术示例:「流转失败。当前状态需要填写【字段名】,该字段不支持自动化补充,请在页面手动填写后通知我继续。」
**4.2 枚举选项的前置查询(批量 + 精准)**
缺失项包含枚举类(select/radio/multi-select/tree-select 等)时:
- 将所有目标 `field_key` 数组**一次性**传入 `workitem meta-fields` 的 `field_keys` 精准查询,拿到 `option_name` 与 `option_id`。**绝不逐页遍历全量配置。**
- 将所有必填项及可选值**汇总为一条消息**向用户展示并询问。
```bash
meegle workitem meta-fields --page-num 1 --project-key 空间key --work-item-type 类型key --field-types '{{field_types}}' --field-keys '["key1","key2"]' --field-query '{{field_query}}' --format json
```
**4.3 字段 Mock 与询问边界(安全底线)**
- **业务决策类、人员、日期、枚举类字段** → **禁止 AI 编造或 mock 数据**。必须列出并询问用户。
- **人员字段(user/multi-user)** → 搜出多个同名或用户未指定时必须确认,**不可默认填当前操作者**(除非用户明确说"分配给我"/"我来处理")。
- **打回/关闭原因等纯说明性文本**(如 "Reopen 原因"、"流转说明")在用户未提供且字段语义不关键时,可默认填 `"重新打开处理"` 或 `"发起流转"`。
**4.4 字段值格式**
拿到用户确认值后,按主文档 [SKILL.md](../SKILL.md)「字段值格式」章节转换为 `field_value`。
> 🚨 **关键约定**:`field_value` 协议层是 **STRING**。标量直接传字符串;数组/对象**必须 JSON.stringify**,否则报 `need STRING type, but got: LIST`。
| 字段类型 | 状态流转场景差异化提示 |
|---------|-----------------|
| `select` / `radio` / `tree-select` | **纯字符串 `option_id`**(🚨 不要传 value/label 的 JSON) |
| `multi-select` | **stringified** `"[{\"option_id\":\"xxx\"}]"`;若字段配置允许新增选项且用户明确提出新值,可生成 8 位随机小写加下划线格式的 option_id 填入 |
| `tree-multi-select` | **stringified 字符串一维数组** `"[\"id1\",\"id2\"]"`(🚨 不可对象数组) |
| `signal` | option_id 字符串(以 `workitem meta-fields` 的 `options[].option_id` 为准;不接受 `"true"`/`"false"`/`"null"`) |
| `file` / `multi-file` | 先调 `attachment +upload`,传 `--resource-type=15`、`--project-key`、`--work-item-id`、`--field-key` 和本地文件路径拿 `file_token`,再 **stringify** 数组 `"[{\"name\":\"a.pdf\",\"type\":\"application/pdf\",\"size\":\"12345\",\"fileToken\":\"<token>\"}]"` |
> 其余通用字段类型(text / number / bool / user / multi-user / date / schedule / precise_date / multi-text / workitem_related_select 等)写入格式详见主文档 [SKILL.md](../SKILL.md)「字段值格式」章节。
> **用户提供的是工作项名称而非 ID** 时,按主文档 [SKILL.md](../SKILL.md)「关联工作项名称 → ID 转换」完整流程(获取目标约束 → `workitem query` 搜索 → 消歧 → 按类型写入)处理。
### STEP 5 — 补完字段后再次流转
所有必填字段通过 `workitem update` 写入后,再次调用 `workflow transition-state` 触发流转:
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --role-operate '{{role_operate}}' --fields '[{"field_key":"xxx","field_value":"yyy"}]' --format json
```
```bash
meegle workflow transition-state --work-item-id 工作项ID --project-key 空间key --transition-id transition_id --format json
```
### STEP 6 — 返回结果
向用户展示:
- 状态变更方向(**从 XX → YY**)
- 自动 / 协助填入的必填项摘要
- 工作项 ID 与(如返回包含)链接
---
## 智能修复(自愈机制)
> 通用自愈规则(格式错误、级联层级、枚举不合法)见 [error-handling.md](error-handling.md)。本 Skill 无额外补充规则。
---
## 熔断与终止
> 通用熔断规则(空间未找到、权限不足)见主文档 [SKILL.md](../SKILL.md)「错误处理」。以下为本 Skill 补充规则:
1. **必填字段全部为硬拦截类型**(投票/计算字段),无法通过接口写入。
2. **同一目标状态**:补字段 → 再次流转连续失败 **> 2 次**。
---
## 常见问题
| 问题 | 处理 |
|------|------|
| 用户只说"关闭这个 bug"没给空间 | 如 URL 可解析则用 URL;否则向用户确认 |
| 可流转状态为空 | 说明当前状态无合法下一步,告知用户在页面核对状态流配置 |
| 用户说"改为 XX"但 XX 不在可流转列表 | 展示当前可流转状态列表,让用户重新选择 |
| 工作项实际是节点流 | 告知用户走 `workflow transition`(action=confirm/rollback),本 Skill 仅处理状态流 |
| 同名字段多个 option | 展示全部 option_name 让用户选,**禁止默认取第一项** |
| 同名人员多个 | 展示列表让用户指定,**禁止默认填当前操作者** |
references/sop-update-workitem.md›
# 更新工作项
> **CRITICAL** — 开始前 MUST 先用 Read 工具读取 `../SKILL.md`,其中包含前置检查、授权流程、命令参数参考、字段值格式、通用规范和错误处理。
本技能用于在飞书项目中更新工作项的字段值或角色成员,全程自动化执行。不包括状态流转和节点流转(由其他 Skill 负责)。
---
## 执行流程
### STEP 1 — 定位工作项并提取修改意图
从用户输入中提取:
- **目标工作项** — URL、工作项 ID 或名称
- **修改内容** — 哪些字段要改成什么值
> **URL 处理**:用户给了 URL 必须先调 `url decode`。只有 `url_kind == workitem_detail` 才能进入本 SOP;其他 kind 按 [url-kinds.md](url-kinds.md) 拒绝或追问。拿到 `simple_name` 和 `work_item_id` 后,必须再调 `project search` 把 `simple_name` 转为权威 `project_key`(同名空间可能有多个)。**禁止**自己从 URL 截取路径段作参数。`work_item_id` 参数必须是字符串类型。
🚨 **获取工作项类型(极重要)**:后续所有查询(字段配置、角色配置等)都强依赖 `work_item_type`。如果用户没有明确告知类型,**必须先调 `workitem get` 获取该实例的真实 `work_item_type`(返回体中的 `work_item_type.key`)**,绝不能猜测为 story 或 issue。
### STEP 2 — 并行查询配置
根据要修改的内容,**一轮内并行发起**:
| 调用 | 条件 | 说明 |
|------|------|------|
| `workitem meta-fields(field_query="字段名")` | 涉及字段修改 | 用 `field_query` 模糊搜索或 `field_keys` 精确匹配,**禁止逐页遍历** |
| `workitem meta-roles` | 涉及角色修改 | 获取角色 key |
| `user search` | 涉及人员字段 | 批量转换姓名为 userkey |
🚨 **效率要求**:必须一轮并发完成所有配置查询,第二轮直接执行更新。禁止逐个串行查询。
### STEP 3 — 转换字段值
将用户给的自然语言值转换成 API 格式(完整格式见主文档 [SKILL.md](../SKILL.md)「字段值格式」):
> 🚨 **关键约定**:`field_value` 协议层是 **STRING**。标量直接字符串化;数组/对象**必须 JSON.stringify**,否则报 `need STRING type, but got: LIST`。
| field_type | 更新场景差异化提示 |
|---|---|
| `user` | 姓名 → `user search` → 单个 userkey 字符串;多人同名时用户说"分配给我自己"用 `current_login_user()`,否则**必须确认** |
| `signal` | `option_id` 字符串(以 `workitem meta-fields` 的 `options[].option_id` 为准;不接受 `"true"`/`"false"`/`"null"`) |
| `workitem_related_multi_select` | **stringified** ID 数组,**禁止写入自身 ID**(防循环引用,触发 `exists loop` 报错) |
| `group_type` | 拉群方式为逻辑字段,读返回判别键 `value`,写协议判别键 `type`(`auto`/`bind`/`disabled`),**读写不对称** |
| `file` / `multi-file` | 先调 `attachment +upload`,传 `--resource-type=15`、`--project-key`、`--work-item-id`、`--field-key` 和本地文件路径拿 `file_token`,再 **stringify** 数组 `"[{\"name\":\"a.pdf\",\"type\":\"application/pdf\",\"size\":\"12345\",\"fileToken\":\"<token>\"}]"` |
> 其余通用字段类型(text / number / bool / multi-user / date / schedule / precise_date / select 系列 / multi-text 等)写入格式详见主文档 [SKILL.md](../SKILL.md)「字段值格式」章节。
> **用户提供的是工作项名称而非 ID** 时,按 [field-value-extras.md](field-value-extras.md)「关联工作项名称 → ID 转换」完整流程(获取目标约束 → `workitem query` 搜索 → 消歧 → 按类型写入)处理。
### STEP 4 — 执行更新
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --role-operate '{{role_operate}}' --fields '[{"field_key":"priority","field_value":"option_id"}]' --format json
```
**角色操作**通过 `role_operate` 参数:
```json
[{"op": "add", "role_key": "角色key", "user_keys": ["userkey1"]}]
```
**拉群方式更新(`group_type` 逻辑字段)**:服务端把 `group_id` / `chat_group` 合并到 `group_type` 单字段,统一通过它读写——**禁止再单独写 `group_id` / `chat_group`**。写协议 `field_value`:
```json
{"type": "auto" | "bind" | "disabled", "group_id": "oc_xxx"}
```
⚠️ **读写不对称**:读返回的判别键是 `value`(如 `{"value":"auto","label":"自动拉群","group_id":"oc_xxx"}`),写要求的判别键是 `type`。不能直接把读到的结构丢回 update。
| `type` | 含义 | 是否带 `group_id` | 客户端预校验 |
|---|---|---|---|
| `auto` | 自动拉群 | 不允许 | 带了 → 阻断(服务端会回 `group_type conflicts with group_id: type=auto`) |
| `bind` | 绑定现有群 | **必填**,非空 `oc_xxx`(空串/纯空格也算缺失) | 缺失或全空白 → 阻断(服务端会回 `group_id is required when group_type=bind`) |
| `disabled` | 不拉群 | 不允许 | 带了 → 阻断(服务端会回 `group_type conflicts with group_id: type=disabled`) |
写法示例(详见 [api-examples.md](api-examples.md) 工作项域):
```bash
meegle workitem update --work-item-id 工作项ID --project-key 空间key --role-operate '{{role_operate}}' --fields '[{"field_key":"group_type","field_value":"{\"type\":\"bind\",\"group_id\":\"oc_xxx\"}"}]' --format json
```
### STEP 5 — 返回结果
展示修改了哪些字段及修改后的值。
---
## 增量追加 (Append SOP)
当用户要求"追加"、"添加"内容时(而非覆盖),必须遵循:
**1. 获取旧值** → 调 `workitem get` 或 `workitem query` 查当前值
**2. 合并新旧值**:
| 字段类型 | 合并方式 |
|---------|---------|
| 文本(`text` / `multi-text`) | 新文本拼接到旧文本后面 |
| 多选枚举(`multi-select`) | 旧选项 + 新选项,`[{"option_id":"xxx"}, ...]` |
| 树状多选(`tree-multi-select`) | 纯字符串一维数组 `["id1", "id2"]`,去重 |
| 关联工作项(`workitem_related_multi_select`) | 旧 ID + 新 ID,去重后写入(只能绑定同空间或白名单空间实例) |
| 多选人员(`multi-user`) | 旧 userkey + 新 userkey,去重 |
| 附件(`multi-file`) | 取旧附件数组,把新 +upload 拿到的 `{name,type,size,fileToken}` 拼上去,整体 stringify 写回(`update` 是覆盖语义,不取旧值会丢历史附件) |
**3. 覆盖写入** → 通过 `workitem update` 写入合并后的值
### 角色追加与删除
| 操作 | `role_operate` 参数 |
|------|-------------------|
| **追加角色人员** | `{"op": "add", "role_key": "xxx", "user_keys": ["userkey"]}` — 不需要先获取旧值 |
| **清空角色人员**(保留角色) | `{"op": "remove", "role_key": "xxx", "user_keys": []}` |
| **彻底删除角色** | `{"op": "remove", "role_key": "xxx", "user_keys": ["_all"]}` |
### 动态 MQL 查询追加
当用户以自然语言条件要求追加关联项(如"将名称包含'依赖'的所有需求追加到前置依赖中")时:
1. 用 `workitem query` 检索符合条件的工作项
2. 提取 ID 列表
3. 按【获取旧值 → 合并 → 写入】流程追加
---
## 边界说明
| 场景 | 处理 |
|------|------|
| **节点级字段**(排期、估分、节点负责人) | 不属于本 Skill,需用 `workflow update-node`。如检测到用户要改节点字段,自动调 `workflow update-node` |
| **角色更新** | 必须通过 `role_operate`,不能放在 `fields` 里 |
| **模板切换**(修改 template) | 高风险操作,这是**唯一需要提醒用户确认**的场景 |
---
## 不可写入的字段类型
以下字段不支持 API 写入,遇到时**直接跳过并告知用户**:
| 类型 | 原因 |
|------|------|
| `vote-boolean`(轻量表态) | 计数器,只能页面操作 |
| `vote-option` / `vote-option-multi`(投票) | 不支持接口伪造 |
| 计算字段 | 系统自动计算,只读 |
> **复合明细表已支持通过 `workitem update` 写入**,但两类协议不同:`compound_field` 使用 stringified action 对象并以 `group_uuid` 定位行;`multi_user_compound_field` 使用 stringified userkey map,且是整体覆盖。更新多人复合字段前必须先读当前值并保留全部人员和非目标数据;该协议只更新当前 map 中已有人员,空值或目标人员不在 map 时不能自动新增人员,必须停止并由用户先通过页面配置人员范围。格式详见主文档 [SKILL.md](../SKILL.md)「字段值格式 → 复合明细表」章节。
**富文本与关联字段**:
- **富文本/多行文本** → 直接传 Markdown 字符串
- **关联云文档** → URL 数组 `["https://xxx.feishu.cn/docx/xxx"]`
- **前置依赖/关联工作项** → 工作项 ID,不同空间可能要求字符串或数字格式,遇类型校验失败立刻切换;用户给的是名称而非 ID 时,按 [field-value-extras.md](field-value-extras.md)「关联工作项名称 → ID 转换」完整流程处理
- **signal 类型** → `option_id` 字符串(以 `workitem meta-fields` 的 `options[].option_id` 为准)
- **级联选项 (tree-select)** → 只传 `option_id` 纯字符串;报 `不满足层级配置` 时查 `children` 树找末级叶子节点,**展示给用户选择**
- **循环引用保护** → 关联字段写入前**必须排查当前工作项自身 ID**,禁止将自身 ID 写入关联项,否则触发 `exists loop`(循环引用)报错
---
## 错误自动恢复(自愈机制)
> 通用自愈规则(格式错误、级联层级、枚举不合法)见 [error-handling.md](error-handling.md)。以下为本 SOP 补充规则:
| 报错特征 | 自愈动作 |
|---------|---------|
| 字段名匹配不到 | 用 `field_query` 模糊搜索取最佳匹配 |
| 枚举值匹配不到 | 模糊匹配 option_name(包含关系),失败则列出所有选项 |
| 角色 key 不确定 | 用 `role_query` 模糊搜索 |
---
## 熔断机制 (Circuit Breaker)
> 通用熔断规则(空间未找到、权限不足)见主文档 [SKILL.md](../SKILL.md)「错误处理」。以下为本 SOP 补充规则:
1. **工作项类型未找到**:`workitem meta-types` 失败超过 3 次
2. **字段转换大面积失败**:转换失败比例 > 60%,终止并列出失败字段明细
---
## 常见问题
| 问题 | 处理 |
|------|------|
| 用户未指定工作项 | 追问 ID 或 URL |
| 字段名匹配不到 | `workitem meta-fields(field_query="关键词")` 模糊查询 |
| 枚举值匹配不到 | 展示所有枚举值让用户选 |
| 人名匹配到多人 | 展示列表让用户指定,**禁止自行选择** |
| 用户要"追加"而非覆盖 | 走增量追加 SOP |
| 用户要改节点字段 | 自动切换到 `workflow update-node` |
references/url-kinds.md›
# URL Kinds —— `url decode` 返回值到 SOP 的映射
## 为什么 skill 不自己拆 URL
Meego/飞书项目的路由非常多,且 snake_case 旧路径、`/meego/` 前缀、`_xxx_resource` 资源工作项、预置功能区(user-gantt / chart / multi-project-view 等)这些都会让"看起来像工作项详情页"的 URL 其实不是。**禁止**自己从 URL 截取路径段作参数。
统一走一条命令:
```bash
meegle url decode --url '<URL>' --format json
```
拿到 `url_kind` 后按本文表格选择 SOP 或回绝。纯本地解析,无网络调用。
---
## 返回字段
| 字段 | 说明 |
|---|---|
| `url_kind` | 必返;未识别时为 `unknown` |
| `simple_name` | 空间标识;需要 `project_key` 时先 `project search` 用 `simple_name` 作为输入 |
| `work_item_type` | 工作项类型 api_name(已脱去 `_xxx_resource` 包装) |
| `work_item_id` | 工作项 ID(字符串) |
| `view_id` / `chart_id` / `plugin_key` / `team_id` / `template_id` | 按路径分别返回 |
| `setting_type` | 设置页子参数(如 permission 类型) |
| `edit_str` | `homepage/edit`、`overview/edit` 的编辑态标记 |
| `is_resource` | true 表示路径里带 `_xxx_resource` 包装 |
| `query` | 原始 query 参数,保留 `scope/node` 等二级导航上下文 |
| `redirected_from` | 若经别名归一化或 `/meego/` 前缀剥离,记录原始路径 |
| `pathname` / `host` / `raw` | 诊断用 |
---
## `url_kind` → 允许的 SOP
### 工作项类
| url_kind | 可用字段 | 推荐 SOP / 命令 |
|---|---|---|
| `workitem_detail` | simple_name · work_item_type · work_item_id | `sop-update-workitem` / `sop-transition-node` / `sop-transition-state` 任一,先 `project search` → `workitem get` |
| `workitem_create` | simple_name · work_item_type | `sop-create-workitem`(`workitem create`) |
| `workitem_draft` | simple_name · work_item_type | 同上,提示用户这是草稿视图 |
| `workitem_homepage` / `workitem_homepage_edit` | simple_name · work_item_type | 无具体工作项 ID — **拒绝**直接操作,要求用户提供详情页 URL 或工作项 ID |
### 视图类(有 `view_id` 但无 `work_item_id`)
| url_kind | 语义 | 处理 |
|---|---|---|
| `view_story` / `view_issue` | 需求 / 缺陷视图 | 如果用户想操作"这个视图里的工作项",要求具体工作项 URL;否则可用 `view get` 查视图 |
| `view_multi_project` / `view_project_overview` / `view_user_gantt` | 跨空间/全域/甘特视图 | 同上 |
| `view_chart` | 图表视图 | 交叉到 `chart_*` 流程 |
| `view_workitem` | 通用工作项视图 | 若 `is_resource=true`,`work_item_type` 已脱包装可直接用 |
### 图表类
| url_kind | 可用字段 | 处理 |
|---|---|---|
| `chart_detail` | simple_name · chart_id | 图表详情;可用 `chart get` |
| `chart_create` | simple_name | 图表创建入口(无 ID) |
| `chart_homepage` / `chart_datascope*` / `chart_penetrate*` | simple_name · chart_id? | 抽屉/子页,通常**不**作为操作目标 |
### 空间/设置类(写操作请走 OpenAPI,非本 skill 范围)
| url_kind | 说明 |
|---|---|
| `project_home` · `project_overview` · `project_empty` · `project_ai_assist` | 空间级落地页,`simple_name` 可用于 `project search` |
| `project_overview_edit` | 编辑态,不作为操作目标 |
| `project_404` · `project_401` · `project_500` | 错误页,**拒绝** |
| `setting_*` | 各类设置页;本 skill 不做设置写操作,**拒绝**并告知 |
| `setting_other` | 未枚举的 setting 子页(前端通过非 exact 路由内部渲染),等同于 `setting_*` — **拒绝** |
| `import_jira` · `import_excel` · `data_recycle` | 导入/回收操作在界面内完成,**拒绝** |
| `plugin_page` | 插件页 — 行为由插件定义,CLI 无法操作,**拒绝** |
### 全局/导航类
| url_kind | 处理 |
|---|---|
| `workbench` · `workspaces` · `favorites` · `inbox` | 顶级导航页,**没有具体目标**,请追问 |
| `teams` · `team_detail` | 团队页;`team_detail` 的 `team_id` 可用于 `team list-members` |
| `templates` · `template_detail` · `template_manage` | 模板中心,本 skill 不做模板操作,**拒绝** |
| `project_list` | 全部空间列表,追问具体空间 |
### 系统域 `/b/*`
| url_kind | 处理 |
|---|---|
| `preference` · `mcp_config` · `mcp_auth` · `ai_hub` · `handover` · `onboarding_*` · `trial_*` · `cross_*` · `slack_connect` · `resource_handover` · `no_project_auth` · `login_datacenter` · `unbundled_register_result` · `b_home` | 系统/管理页,本 skill **拒绝**业务操作 |
### 登录/外部入口
| url_kind | 处理 |
|---|---|
| `login_fetch_cookie` · `login_asset` · `switch_asset` · `home_ka` · `tenant_select` · `tenant_create` · `channel_error` | 登录相关 — 改走 `auth-guard` |
| `quick_create_form` · `issue_trans` · `issue_create_open_usecase` · `story_create_open` · `jump_to_outer` · `light_share` · `ai_application_share` | 飞书内嵌入口,本 skill 通常不作为操作起点 |
### 错误兜底
| url_kind | 处理 |
|---|---|
| `lark_page_404` · `project_empty_page` · `route_loading` · `system_upgrade` | 错误页,**拒绝** |
| `unknown` | **拒绝**并要求用户提供详情页 URL 或直接描述任务 |
### 特殊字段校验
- `redirected_from` 非空 → 在回复中提一句"检测到旧版路径,已自动归一化",避免用户误以为 URL 错了
- `is_resource=true` → 告知用户这是资源工作项视图,`work_item_type` 已自动脱去 `_xxx_resource` 包装
- `query.scope` / `query.node` 非空 → 仅作为导航上下文,不要当作业务参数
---
## 典型分支模板(供 SOP 引用)
```
STEP 0 — URL 解析(仅当用户提供了 URL)
url decode --url "<URL>"
SAVE $url_kind, $simple_name, $work_item_type, $work_item_id, $view_id, $redirected_from
SWITCH $url_kind:
- workitem_detail → GOTO 本 SOP 的 STEP 1(已具备 simple_name + work_item_id)
- workitem_homepage → ASK user:"需要具体工作项 URL,这是类型主页。";STOP
- view_* → ASK user:"这是视图 URL,请粘贴具体工作项详情页 URL。";STOP
- unknown → ASK user:"无法识别该 URL,请确认后重发或直接描述任务。";STOP
- 其他非本 SOP 范围 → 告知 kind,建议对应操作;STOP
```
references/url-links.md›
# 生成 Meegle 页面链接
## 使用边界
仅在用户明确要求链接,或链接本身是交付物时使用。普通查询、创建、更新、流转完成后不要自动追加链接。
目标是交付当前用户可打开的 canonical 页面链接:
1. 先固定执行上下文和 `$target_host`,再通过 [Auth Guard](auth-guard.md) 取得有效 host:用户显式选择 `--profile` 时,Auth Guard 内的命令与后续所有查询都传同一个全局 flag;未显式选择时,全程使用当前 profile 且禁止中途切换。随后确认目标对象对该身份可查询;同一轮只有执行上下文与 host 都相同时才能复用成功结果。
2. 只使用本文列出的路径模板,不根据印象发明路由。
3. 生成后必须调用 `url decode` 反向校验;返回的 `url_kind` 与目标类型不同或为 `unknown` 时,不得交付该链接。
反向解析只证明链接结构正确。目标查询成功才是“当前身份可访问”的证据;它不保证其他用户也有权限。
---
## 参数来源
### host
先确定用户要打开的目标 host,并保存为本次请求的 `$target_host` 后再执行 Auth Guard:
1. 用户明确指定的域名;
2. 用户提供的已有 Meegle URL 经 `url decode` 返回的 `host`;
3. 未指定时,SAVE `$target_host = null`,使用 Auth Guard 返回并保存的有效 `$host`(它已按运行环境与所选 profile 解析)。
在查询目标对象前执行 Auth Guard,并复用它的 host 一致性结论:域名忽略大小写并去掉末尾的 `.`,端口必须一致。若不一致,请用户切换到目标环境对应的 profile 或重新登录;不要拿环境 A 的成功查询证明环境 B 的链接可访问,也不要在下游用未经规范化的原字符串重复比较。最终链接只能使用这次成功查询实际连接的 host。
host 为空或不是可信的 Meegle/飞书项目域名时,询问用户,不要默认猜成 `meegle.com` 或 `project.feishu.cn`。host 只保留域名和可选端口,不带协议、路径、query 或 fragment;最终统一使用 `https://`。
### simple_name
`simple_name` 与 `project_key` 不是同一个概念,禁止互换。使用 `project search` 的唯一空间结果作为权威来源:
- 已有 URL 的 `simple_name` 只能作为空间查询输入;只有该 URL 本身指向同一目标对象/视图,或查询结果证明它与目标对象的 `project_key` 属于同一空间时,才能复用;
- 只有空间名称或 project_key:查询后使用唯一命中结果中的 `simple_name`;
- 结果不唯一或没有返回 `simple_name`:询问用户提供准确空间或现有页面链接,不要拿 `project_key` 代替。
### 其他动态字段
ID 与类型 key 优先取自本轮已有命令结果;否则先用对应查询命令确认:
| 目标 | 可访问性检查 |
|---|---|
| 工作项详情 | `workitem get` |
| 需求、缺陷、通用、空间概览、甘特、图表视图 | `view get` |
| 跨空间 / 全景视图 `view_multi_project` | `view list-multi-project-workitems` |
| 图表详情 | `chart get` |
| 空间首页 / 空间概览 | `project search` |
工作项类型、视图 ID、图表 ID 或工作项 ID 缺失时合并为一次询问。查询返回无权限、不存在或不唯一时停止,不生成“可访问”链接。
视图查询成功只能证明该 `view_id` 可读取,不能单独推断页面路由类型。`url_kind` 必须来自指向同一视图的已有 URL 经 `url decode` 的结果,或查询结果中的权威 view scope;用户口述的视图类型只能用于缩小查询范围,不能单独作为路由证据。无法确认时询问用户。特别是“全景视图”必须确认是 `view_multi_project` 还是 `view_project_overview`。
---
## 支持的 canonical 路径
每个 `{...}` 仅表示一个路径段。动态值按 RFC 3986 path segment 编码:只保留字母、数字、`-`、`.`、`_`、`~`,其余字节全部百分号编码;拒绝原始值中包含 `/`、`\\`、`?`、`#`、控制字符,或整个值为 `.` / `..` 的歧义输入。
| url_kind | 必需字段 | pathname |
|---|---|---|
| `workitem_detail` | simple_name, work_item_type, work_item_id | `/{simple_name}/{work_item_type}/detail/{work_item_id}` |
| `view_story` | simple_name, view_id | `/{simple_name}/storyView/{view_id}` |
| `view_issue` | simple_name, view_id | `/{simple_name}/issueView/{view_id}` |
| `view_workitem` | simple_name, work_item_type, view_id | `/{simple_name}/workObjectView/{work_item_type}/{view_id}` |
| `view_multi_project` | simple_name, view_id | `/{simple_name}/multiProjectView/{view_id}` |
| `view_project_overview` | simple_name, view_id | `/{simple_name}/project-overview/{view_id}` |
| `view_user_gantt` | simple_name, view_id | `/{simple_name}/userGantt/{view_id}` |
| `view_chart` | simple_name, view_id | `/{simple_name}/workObjectView/chart/{view_id}` |
| `chart_detail` | simple_name, chart_id | `/{simple_name}/chart/detail/{chart_id}` |
| `project_home` | simple_name | `/{simple_name}` |
| `project_overview` | simple_name | `/{simple_name}/overview` |
资源工作项或资源视图把 `work_item_type` 包装为 `_{work_item_type}_resource`,例如 `story` → `_story_resource`。不要重复包装已经带该形式的值。
`work_item_type=chart` 的工作项详情会与 `chart_detail` 冲突;通用视图中的 `work_item_type=chart` 会与 `view_chart` 冲突。反向校验会暴露这类保留路由冲突,遇到时停止并说明当前 canonical 路由无法无歧义表达目标。
不支持系统页、设置页、登录页、导入页、错误页、插件页,以及任何本文未列出的 `url_kind`。
---
## 生成与校验
每个候选链接都必须校验完整目标身份,不能只比较 `url_kind`:
- 所有类型:decoder 返回的 `host` 按 Auth Guard 的规则规范化后必须等于 `$host`,`simple_name` 必须等于权威空间结果;
- `workitem_detail`:`work_item_type`、`work_item_id` 必须与目标一致;资源工作项还必须返回 `is_resource=true`;
- `view_workitem`:`work_item_type`、`view_id` 必须与目标一致;资源视图还必须返回 `is_resource=true`;
- 其他 `view_*`:`view_id` 必须与目标一致;
- `chart_detail`:`chart_id` 必须与目标一致;
- `project_home` / `project_overview`:除公共字段外没有额外 ID。
任一字段缺失或不一致都停止交付。动态路径段比较解码后的语义值,不比较百分号编码文本本身。
### 工作项详情
已知:host=`project.feishu.cn`、simple_name=`xopenapp`、work_item_type=`story`、work_item_id=`7092625364`。
候选链接:
```text
https://project.feishu.cn/xopenapp/story/detail/7092625364
```
校验:
```bash
meegle url decode --url 'https://project.feishu.cn/xopenapp/story/detail/7092625364' --format json
```
只在输出 `url_kind=workitem_detail` 且关键字段与输入一致时交付。
### 通用工作项视图
```text
https://project.feishu.cn/xopenapp/workObjectView/story/9988
```
必须解码为 `view_workitem`,并返回一致的 `simple_name`、`work_item_type` 和 `view_id`。
### 跨空间 / 全景视图
```text
https://project.feishu.cn/xopenapp/multiProjectView/9988
https://project.feishu.cn/xopenapp/project-overview/9988
```
分别必须解码为 `view_multi_project` 和 `view_project_overview`。不要只凭“全景视图”名称猜一种;根据视图查询结果或已有 URL 确定类型,无法区分时询问用户。
---
## 交付格式与失败处理
成功时优先用固定的“类型 + ID”作 Markdown label,例如:
```markdown
[需求 7092625364](https://project.feishu.cn/xopenapp/story/detail/7092625364)
```
若必须使用查询结果中的名称作为 label,先转义 `\\`、`[`、`]`、`(`、`)` 等 Markdown 元字符,避免不可信名称改变链接结构。
失败时:
- host 缺失:请用户指定环境域名或配置当前 profile;
- `simple_name` 缺失/不唯一:请用户确认空间,禁止用 `project_key` 替代;
- 目标不存在或无权限:说明当前身份无法验证可访问性,不返回伪链接;
- decoder 返回 `unknown` 或另一 `url_kind`:报告路由冲突或模板不支持,不返回候选链接;
- `url decode` 报 unknown command:说明本地 CLI 版本过旧,建议升级;不要绕过校验后手拼交付。
以上均为确定性失败;输入与环境没有变化时不要原样重试。
references/view.md›
# 视图辅助命令
视图搜索与固定视图管理。读取视图数据用 `view get`(见 SKILL.md 主文件)。
## view search
按名称搜索视图。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --view-scope | string | 是 | 视图范围 |
| --key-word | string | 是 | 关键词 |
## view create-fixed
创建固定视图。上限 200 个工作项。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --name | string | 是 | 视图名称 |
| --work-item-type | string | 是 | 工作项类型 |
| --work-item-id-list | array | 是 | 工作项 ID 列表 |
## view update-fixed
更新固定视图。add/remove_work_item_ids 二选一。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --view-id | string | 是 | 视图 ID |
| --work-item-type | string | 是 | 工作项类型 |
## view list-multi-project-workitems
查看全景视图(multiProjectView)下当前用户有权限的工作项列表。全景视图的链接上带有 `multiProjectView` 关键字,可从中提取 `view_id`。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --view-id | string | 是 | 全景视图 ID |
| --page-num | number | 否 | 分页页码,每页 50 条,从 1 开始 |
references/wbs.md›
# WBS 计划表
计划表(Work Breakdown Structure)有两套数据模型:
- **草稿(draft)** — 当前用户的可编辑副本。`wbs create-draft` 创建、`wbs edit-draft` 原子编辑(单行或批量操作)、`wbs publish-draft` 发布、`wbs reset-draft` 放弃修改。
- **实例(instance)** — 已发布的线上版本。只读,用 `wbs list-instance-rows` 查询。
**常见流程**:`wbs create-draft` → 多次 `wbs edit-draft` → `wbs publish-draft`。
**辅助命令**(创建草稿 / 重置 / 进度 / 模板)请见 [misc.md](misc.md)。
---
## 共用查询能力(draft / instance)
`wbs list-draft-rows` 与 `wbs list-instance-rows` 参数完全一致,仅查询的数据集不同——前者查当前用户的草稿,后者查线上实例。**禁止混用**:例如先用 `wbs list-draft-rows` 取行 uuid,后续递归 / 编辑也必须继续用草稿系列命令。
### condition_query 支持字段
筛选行用 `condition_query`(object)。**仅支持以下字段**:
| 字段 | 含义 | 取值 |
|------|------|------|
| `wbs_name` | 行名称 / 任务名称 / 排期项名称 / 子项名称 | string |
| `wbs_parent_id` | 父级 uuid(用于查子级 / 下级 / 子任务 / 子项) | uuid |
| `wbs_belong_status` | 所属状态 / 阶段(计划 / 开发 / 验证 / 发布等) | string |
| `wbs_states_doing` | 当前状态 / 任务状态 | `not_started` / `doing` / `finished` |
| `wbs_role` | 角色(可多人) | string |
| `wbs_owner_in_charge` | 负责人 / 责任人(可多人) | userkey;查"我负责的"先调 `user search` 取 userkey |
| `wbs_delay_label` | 延期标识 | `delay` / `normal` |
| `wbs_milestone_node_type` | 节点类型 | `milestone` / `normal_node` / `key_path_node` |
| `wbs_deletable` | 允许删除节点 | bool |
**递归查子级 SOP**:用户提到"子级 / 所有子 / 下级"时,先按条件筛出目标行取 `uuid`,再用 `wbs_parent_id` + `In` 查直接子级(多个 uuid 逗号分隔),再以下一层 uuid 继续递归直到无子级。**全流程必须使用同一工具**(草稿就一直草稿,实例就一直实例)。
### row_field_list 返回字段控制
`row_field_list`(string[])按需指定返回字段。为空时默认返回 `base.*` + `meta.uuid`;`["_all"]` 返回全量字段。
| 通配符 | 包含字段 |
|--------|----------|
| `meta.*` | `uuid`、`parent_id`、所属工作项信息等 |
| `base.*` | `name`(行名)、`owners`(负责人)、`start_time` / `end_time`(实际开始 / 完成时间)、`schedule`(排期)、`schedule_dependency`(排期依赖)、`union_deliveries`(交付物)、`process_status`(当前状态) |
| `node_extra.*` | 普通节点扩展:里程碑、所属状态、节点唯一 id `state_key`、前序节点等 |
| `sub_instance_extra.*` | 子实例扩展:拆解模式 `dismantle_mode` |
**查工作项字段 / 节点字段**:先用 `workitem meta-fields` 判断是否为工作项字段、用 `workflow meta-node-fields` 判断是否为节点字段;再从计划表行中取对应 `workitem_id` / `state_key`,基于这些 ID 继续查字段值。
### wbs list-draft-rows
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID(**字符串**);URL 自动解析;名称需先调 `workitem get` |
| --project-key | string | 是 | 空间 key |
| --condition-query | object | 否 | 筛选条件,仅支持上表字段 |
| --need-structure | boolean | 否 | 是否返回树状层级。默认 `false`;查 / 编辑子级时设为 `true` |
| --page-no | number | 否 | 页号,从 1 开始;返回 `has_more` 时需翻页 |
| --page-size | number | 否 | 页大小,1–50,默认 25。超过 1000 行需分页合并 |
| --row-field-list | string[] | 否 | 见上表 |
### wbs list-instance-rows
参数与 `wbs list-draft-rows` 完全一致,仅查询数据集不同(线上已发布实例)。
---
## wbs edit-draft
对计划表草稿执行**一次原子编辑**。一次调用只能传一个 `operation_type`;该操作内部可通过 `items` / `uuids` 等数组承载单行或批量编辑。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID |
| --project-key | string | 是 | 空间 key |
| --operation | object | 是 | 操作对象,结构因操作类型而异 |
### operation 结构
`operation` 是一个对象,统一形如:
```json
{
"operation_type": "<动作 PascalCase>",
"operation_value": {
"<动作 snake_case>": { /* 动作特定字段 */ }
}
}
```
`operation_value` 下的子 key 必须与 `operation_type` 匹配;优先按下表传,避免只做机械 snake_case 猜测。
| operation_type | operation_value 子 key | 用途 |
|----------------|------------------------|------|
| `AddTaskRows` | `add_task_rows` | 批量新增普通任务行 |
| `AddNodeRows` | `add_node_rows` | 批量新增资源节点行 |
| `AddSubInstanceRows` | `add_sub_instance_rows` | 批量新增子工作项行 |
| `AddResourceSubInstanceRows` | `add_resource_sub_instance_row` | 批量新增资源子实例行(注意子 key 里的 `row` 为单数) |
| `DeleteRows` | `delete_rows` | 批量删除 / 移出行 |
| `RestoreRows` | `restore_rows` | 批量恢复行 |
| `UpdateDismantleMode` | `update_dismantle_mode` | 切换拆解模式 |
| `AdjustRowOrder` | `adjust_row_order` | 调整行排序 / 父级 |
| `UpdateNodeSequence` | `update_node_sequence` | 修改资源节点前后序 |
| `UpdateNodePhases` | `update_node_phases` | 批量修改节点所属阶段 |
| `UpdateName` | `update_name` | 修改名称 |
| `UpdateRoleOwners` | `update_role_owners` | 批量修改角色负责人 |
| `UpdateDelivery` | `update_delivery` | 修改交付物 |
| `UpdatePlannedSchedule` | `update_planned_schedule` | 修改单行排期(旧单行形态) |
| `UpdatePlannedSchedules` | `update_planned_schedules` | 批量修改计划排期 |
| `UpdateSchedulePoints` | `update_schedule_points` | 批量修改估分 |
| `UpdateScheduleActualTimes` | `update_schedule_actual_times` | 批量修改实际工时 |
**选择要点**:
- 用户说普通任务 / 子任务 / 任务行时用 `AddTaskRows`;其 `parent_uuid` 必须是当前计划表中允许新增子任务的父行,并非任意 `sub_instance` / `node` / `sub_task` 都可用。若后端返回 `can not add sub task under parentUUID` 或 `wbs task not found`,说明父行不支持该新增位置,需换父行或先确认拆解结构。
- 用户说工作项类型(如"需求"、"项目活动")时用 `AddSubInstanceRows`,并先调 `workitem meta-types` 确认 `work_item_type_key`。
- 涉及资源节点或资源子实例时,先调 `wbs list-element-templates` 查询模板 / `element_key`;资源节点通常用 `AddNodeRows`,资源子实例用 `AddResourceSubInstanceRows`。
- `UpdateRoleOwners` 与 `UpdateDelivery` 是全量覆盖语义。追加负责人或交付物前,先用 `wbs list-draft-rows` 取当前值并合并历史值;不要只传新增值。
- 排期时间写 ISO8601 带时区字符串(如 `2026-04-22T00:00:00+08:00`);不要传毫秒时间戳,也不要在 `specify_schedule` 外额外包一层 `schedule`。
**示例:批量新增普通任务行**
```json
{
"operation_type": "AddTaskRows",
"operation_value": {
"add_task_rows": {
"parent_uuid": "<上级行 uuid,来自 wbs list-draft-rows>",
"pre_uuid": "",
"items": [
{ "name": "新任务名" }
]
}
}
}
```
**示例:批量新增资源节点行**
```json
{
"operation_type": "AddNodeRows",
"operation_value": {
"add_node_rows": {
"parent_uuid": "<上级行 uuid>",
"pre_uuid": "",
"phase": "started",
"items": [
{ "element_key": "<来自 wbs list-element-templates 的 element_key>" }
]
}
}
}
```
**示例:批量新增子工作项行**
```json
{
"operation_type": "AddSubInstanceRows",
"operation_value": {
"add_sub_instance_rows": [
{
"parent_uuid": "<上级行 uuid>",
"work_item_type_key": "<来自 workitem meta-types 的工作项类型 key>",
"fields": [
{ "field_key": "name", "field_value": "子工作项名称" }
]
}
]
}
}
```
**示例:批量新增资源子实例行**
```json
{
"operation_type": "AddResourceSubInstanceRows",
"operation_value": {
"add_resource_sub_instance_row": [
{
"parent_uuid": "<上级行 uuid>",
"work_item_type_key": "<资源工作项类型 key>",
"resource_work_item_ids": ["<已有资源实例 ID>"]
}
]
}
}
```
**示例:批量修改计划排期**
```json
{
"operation_type": "UpdatePlannedSchedules",
"operation_value": {
"update_planned_schedules": {
"uuids": ["<行 uuid>"],
"different_schedule": false,
"specify_schedule": {
"estimate_start": "2026-04-22T00:00:00+08:00",
"estimate_finish": "2026-04-25T23:59:59+08:00"
}
}
}
}
```
**示例:批量修改估分 / 实际工时**
```json
{
"operation_type": "UpdateSchedulePoints",
"operation_value": {
"update_schedule_points": {
"uuids": ["<行 uuid>"],
"different_schedule": false,
"schedule_point": {
"schedule_point_value": 3,
"schedule_point_value_unit": 8
}
}
}
}
```
```json
{
"operation_type": "UpdateScheduleActualTimes",
"operation_value": {
"update_schedule_actual_times": {
"uuids": ["<行 uuid>"],
"different_schedule": false,
"actual_time": {
"value": 5,
"unit": 8
}
}
}
}
```
**示例:批量删除 / 恢复行**
```json
{
"operation_type": "DeleteRows",
"operation_value": {
"delete_rows": {
"uuids": ["<行 uuid>"],
"delete_action_type": "delete",
"reason": "重复行"
}
}
}
```
```json
{
"operation_type": "RestoreRows",
"operation_value": {
"restore_rows": {
"uuids": ["<行 uuid>"]
}
}
}
```
**示例:批量修改阶段 / 负责人**
```json
{
"operation_type": "UpdateNodePhases",
"operation_value": {
"update_node_phases": {
"phase": "started",
"uuids": ["<行 uuid>"]
}
}
}
```
```json
{
"operation_type": "UpdateRoleOwners",
"operation_value": {
"update_role_owners": {
"uuids": ["<行 uuid>"],
"owners": ["<userkey>"]
}
}
}
```
调用后响应里若有 `change_uuids`,可直接拿去做下一步 `wbs edit-draft`(如改排期)或 `wbs publish-draft` 的部分发布。
**建议工作流**:
1. `wbs list-draft-rows` 取目标行 / 父行的 `uuid` 与当前字段值
2. 调 `wbs edit-draft` 一次执行一种操作
3. 如调用返回了 `operation_id`,先用 `wbs get-draft-progress` 轮询完成再进行下一次编辑——多个 `wbs edit-draft` 并发或不等异步完成就连发可能丢操作
4. 全部改完用 `wbs publish-draft` 发布
---
## wbs publish-draft
将编辑完成的草稿发布到线上。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
| --uuid-strings-list | string[] | 否 | 要发布的行 uuid 列表。**部分发布**:传 uuid 列表,无需二次确认。**全量发布**:不传此字段(或传 `["_all"]`),必须先用以下**固定话术**二次确认,用户同意后才执行 |
### 全量发布二次确认(固定话术)
> 本人及协同者的全部编辑内容均会被发布,请确认是否全量发布?
部分发布(传入 `uuid_strings_list`)**不需要**二次确认,直接执行。
---
## 异步操作进度
`wbs create-draft` / `wbs edit-draft` / `wbs publish-draft` / `wbs reset-draft` 返回 `operation_id` 后,需用 `wbs get-draft-progress` 轮询进度。参数表见 [misc.md](misc.md#wbs-辅助命令)。
references/workflow.md›
# 工作流辅助命令
工作流流转之前用来查询可流转方向、必填项、节点字段配置的辅助命令。核心流转命令(`workflow transition` / `workflow transition-state` / `workflow get-node` / `workflow update-node`)见 SKILL.md 主文件。
## workflow list-state-transitions
查看工作项可流转的状态列表。状态流流转前必须先调用此命令拿 `transition_id`。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
| --work-item-type | string | 是 | 工作项类型 |
| --user-key | string | 是 | 用户标识 |
## workflow list-state-required
查看流转所需的必填信息(节点流传 node_key,状态流传 state_key)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
| --state-key | string | 是 | 节点流的 node_key 或状态流的 state_key |
| --mode | string | 否 | 默认查所有必填项;传 `unfinished` 仅查未完成必填项 |
## workflow meta-node-fields
查看节点字段配置。`workflow update-node` 修改节点自定义字段前用来确认合法 field_key、字段类型与 options。查询评审结论选项时按 `field_keys=["node_finished_conclusion"]` 精确查询,并从返回配置的 options / 选项列表中取合法值。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-type | string | 是 | 工作项类型 |
| --field-keys | array | 否 | 精确匹配节点字段 key 或名称,如 `["node_finished_conclusion"]` |
| --field-types | array | 否 | 按节点字段类型筛选 |
| --query | string | 否 | 按字段 name / key 模糊搜索 |
references/workitem.md›
# 工作项元数据命令
查询工作项类型、字段、角色配置的辅助命令。在 `workitem create` / `workitem update` / `workitem query` 之前用来确认合法 key。
## workitem meta-types
获取指定空间下所有工作项类型列表。用户描述模糊时用此命令确认合法 type_key。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 projectKey |
## workitem meta-fields
获取指定空间和工作项类型的可用字段配置(不含禁用字段和角色配置)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-type | string | 是 | 工作项类型 key 或名称 |
| --page-num | number | 是 | 页数,每页 50 条,从 1 开始 |
| --field-keys | array | 否 | 精确匹配字段 key 或名称 |
| --field-query | string | 否 | 模糊查询字段 key 和名称 |
| --field-types | array | 否 | 按字段类型筛选 |
## workitem meta-roles
获取指定工作项类型的角色列表。用于查询/创建/更新工作项前确认合法 role_key。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-type | string | 是 | 工作项类型 key 或名称 |
| --page-num | number | 是 | 页数,每页 50 条,从 1 开始 |
| --role-keys | array | 否 | 精确匹配角色 key 或名称 |
| --role-query | string | 否 | 模糊查询角色 key 和名称 |
## workitem meta-create-fields
查看创建工作项时可用的字段及类型。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-type | string | 是 | 要创建的工作项类型 key |
SKILL.md›
---
name: meegle
description: |
飞书项目(Meego/Meegle)操作工具。支持查询和管理工作项、节点流转、视图查询、个人待办、排期统计等功能。 Use when user needs to work with Feishu/Lark Meego project management — including querying work items, creating/updating work items, completing workflow nodes, checking views, listing todos, analyzing schedules/workloads, or searching with MQL. 关键词:飞书项目、meego、meegle、工作项、需求、任务、缺陷、排期、视图、待办、节点。
---
# 飞书项目 (Meego/Meegle) 操作指南
本技能通过 Meegle CLI来操作飞书项目数据。输出语言跟随用户输入语言,默认中文。
> 各命令的调用示例见 [references/api-examples.md](references/api-examples.md)。
> **授权流程**(所有业务命令前必须执行):见 [references/auth-guard.md](references/auth-guard.md)
> **CLI 使用指南**(命令结构、参数传递、命令发现):见 [references/cli-guide.md](references/cli-guide.md)
---
## Project 空间域
### project search
搜索空间信息,将空间名转换为 project_key 或验证空间是否存在;省略 --project-key 时返回当前用户最近访问过的空间列表(按访问时间由近及远)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 否 | 空间 projectKey、simpleName 或空间名称;留空查询当前用户可访问的空间 |
| --page-num | number | 否 | 分页页码,每页 50 条,从 1 开始 |
---
## WorkItem 工作项域
> 元数据查询命令(`workitem meta-types` / `workitem meta-fields` / `workitem meta-roles` / `workitem meta-create-fields`)的参数表见 [references/workitem.md](references/workitem.md)。
### workitem create
创建工作项实例。**务必先用 `workitem meta-fields` 获取字段信息,`workitem meta-roles` 获取角色信息。模板 ID 是必填项。**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-type | string | 是 | 工作项类型 |
| --project-key | string | 否 | 空间标识 |
| --fields | array | 否 | 字段值列表,每项含 field_key 和 field_value |
### workitem get
按 ID/名称查询工作项概况。不传 fields 时返回固定基础字段加上一组默认系统字段:`group_type`(拉群方式)、`description`、`current_status_operator`、`watchers`(value 为 null 时也会出现);其余字段需先通过 `workitem meta-fields` 拿到 key 再传入 fields。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 否 | 工作项 ID(与 `--name` 二选一) |
| --name | string | 否 | 按名称查询工作项(与 `--work-item-id` 二选一) |
| --project-key | string | 否 | 空间 key |
| --fields | array | 否 | 要查询的 field_key 或 field_name;传 `["_all"]` 时按逻辑字段分页返回全部字段;传 `["group_type"]` 时只取拉群方式 |
| --page-size | number | 否 | 仅 `fields=["_all"]` 时生效;每页字段数量,默认 100,最大 200。**Meegle CLI 序列化约束**:`--page-size N` 会被序列化为字符串触发后端 `need I64 type, but got: STRING`;必须走 `--params '{"page_size":N}'` 以数字传出 |
| --page-token | string | 否 | 仅 `fields=["_all"]` 时生效;翻页 token,首次不传,下一页传上一页响应的 `next_page_token`(token 形如字段 key,例如 `"business"`);同上,须走 `--params '{"page_token":"..."}'` |
> **逻辑字段聚合(重要心智模型)**:服务端把 `group_id` / `chat_group` 这类"拉群"相关的物理字段**合并**到一个逻辑字段 `group_type`。读取/更新统一走 `group_type`,**不要再单独读取 `group_id` 或 `chat_group`**。
>
> ⚠️ **读写协议不对称**:读返回结构里**判别键是 `value`**(不是 `type`),更新时**判别键是 `type`**——禁止照着读到的结构直接回写。
>
> 读返回(`workitem_fields[].value` 字段)的形状:
> - `auto` → `{value: "auto", label: "自动拉群", group_id: "oc_xxx"}`(自动拉群附带 group_id;状态切换时 oc_id 可能会变)
> - `bind` → `{value: "bind", label: "绑定现有群", group_id: "oc_xxx"}`
> - `disabled` → `{value: "disabled", label: "不拉群"}`(无 group_id)
>
> 写协议(`field_value` 里的 JSON):`{"type": "auto" | "bind" | "disabled", "group_id": "oc_xxx"}`
### workitem +batch-get
批量查询工作项(Meegle CLI 客户端 fan-out:并发调用 `workitem get`)。单次 ≤ 200 个 ID,3 并发,返回 `{results, errors, summary}`;ID 量大时用 `--format ndjson` 流式输出。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-ids | array | 二选一 | 工作项 ID 列表(逗号分隔或多次传入) |
| --ids-file | string | 二选一 | 从文件读取 ID(一行一个,`#` 开头注释) |
| --fields | array | 否 | 要查询的 field_key 列表 |
| --project-key | string | 否 | 空间 key |
### workitem update
修改指定实例的字段值或角色。节点字段更新须用 `workflow update-node`。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID 或名称 |
| --project-key | string | 否 | 空间 key |
| --fields | array | 否 | 要更新的字段列表,每项含 field_key 和 field_value |
| --role-operate | array | 否 | 角色操作,每项含 op(add/remove)、role_key、user_keys |
**角色更新**:不能通过 fields 更新角色,必须用 `role_operate`。role_key 通过 `workitem meta-roles` 获取,user_keys 通过 `user search` 获取。
**拉群方式更新(`group_type` 逻辑字段)**:要修改/读取拉群方式统一走 `group_type`,不要再单独操作 `group_id` / `chat_group`。写协议 `field_value` 形如:`{"type": "auto" | "bind" | "disabled", "group_id": "oc_xxx"}`(注意写用 `type` 作为判别键,**与读返回的 `value` 不对称**)。校验规则(服务端实际报错文本):`bind` 不带 `group_id` 或带空串/纯空格 → `group_id is required when group_type=bind`;`auto`/`disabled` 同时带 `group_id` → `group_type conflicts with group_id: type=<auto|disabled>`。详细示例见 [references/sop-update-workitem.md](references/sop-update-workitem.md)。
### workitem query
使用 MQL 查询工作项数据。语法详见 [references/mql-syntax.md](references/mql-syntax.md)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间标识(支持名称、simpleName、projectKey) |
| --mql | string | 是(翻页时可用 session_id 替代) | MQL 查询语句(完整 SQL) |
| --session-id | string | 否 | 分页会话 ID,传入后不解析 MQL 直接翻页 |
| --group-pagination-list | array | 否 | 分组分页信息,首次查询可不传;翻页时传 `[{ "group_id": "分组ID", "page_num": 页码 }]` |
**分组分页**:
- `--group-pagination-list` 是数组,当前只支持传一组分页数据;元素结构为 `{ "group_id": string, "page_num": number }`
- `group_id` 取首查返回的 `list[].group_infos[].group_id`;无分组查询返回的默认分组 ID 为 `"1"`,翻页时也传 `"1"`
- `page_num` 从 1 开始;MQL 首查不传分页参数时默认返回第一页,单页最多 50 条。当前接口没有 `page_size` / `page_token` 子字段
- 翻页时传首查返回的 `session_id` 和目标分组的分页参数;传 `session_id` 后后端不再解析 MQL,只按已有会话取对应分组页
**要点**:
- 先用 `workitem meta-fields` / `workitem meta-roles` 获取字段与角色配置;查不到直接报错不要继续
- SELECT 后属性不宜过多,**优先使用字段 key**(如 `name`、`priority`、`status`);返回按页返回,需全量时使用翻页参数
### workitem list-op-records
查看工作项操作记录。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --work-item-id | string | 是 | 工作项 ID |
---
## Attachment 附件域
附件上传/下载分两步:先调 `attachment prepare-upload` / `attachment prepare-download` 申请带签名的对象存储 URL,再与对象存储做 HTTP 直连。Meegle CLI 提供 `attachment +upload` / `attachment +download` 一键封装。详细参数表与流程说明见 [references/attachment.md](references/attachment.md)。
---
## WorkFlow 工作流域
> 流转辅助命令(`workflow list-state-transitions` / `workflow list-state-required` / `workflow meta-node-fields`)的参数表见 [references/workflow.md](references/workflow.md)。
### workflow transition
仅用于节点流工作项,操作节点完成流转或回滚。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID |
| --action | string | 否 | confirm(流转) / rollback(回滚) |
| --node-id | string | 否 | 节点 ID |
| --rollback-reason | string | 否 | 回滚原因,action=rollback 时需填写 |
| --project-key | string | 否 | 空间 key |
### workflow transition-state
仅用于状态流工作项,流转工作项状态。先用 `workflow list-state-transitions` 获取可流转状态及 transition_id。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID |
| --transition-id | string | 否 | 状态流转 ID,从 `workflow list-state-transitions` 获取 |
| --project-key | string | 否 | 空间 key |
### workflow get-node
获取工作项中指定节点或所有节点的完整详情。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID 或名称 |
| --node-id-list | array | 否 | 节点 ID 列表,传空或 `_all` 获取所有节点 |
| --field-key-list | array | 否 | 节点字段 key,传空或 `_all` 获取所有字段 |
| --need-sub-task | boolean | 否 | 是否需要节点子项(子任务) |
| --page-num | number | 否 | 节点信息一次最多 20 个,按页返回 |
| --project-key | string | 否 | 空间 key |
### workflow update-node
修改节点(排期、负责人、自定义字段等)。排期/差异化排期/负责人不要同时修改,需分多次调用。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID |
| --node-id | string | 是 | 节点 ID(node_key) |
| --node-owners | array | 否 | 节点负责人 userkey 数组;清空传空数组 `[]` |
| --node-schedule | object | 否 | 节点排期,格式 `{"estimate_start_date":ms,"estimate_end_date":ms,"owners":[userkey],"points":数字}`;清空传 `{}`;不变更则不传 |
| --schedules | array | 否 | 按人差异化排期,每项细化到单个人的排期;清空某人则 `estimate_start_date`/`estimate_end_date` 传 null |
| --fields | array | 否 | 节点自定义字段,每项含 `field_key` 和 `field_value`(STRING 协议,见「字段值格式」) |
| --project-key | string | 否 | 空间 key |
---
## MyWork 工作台域
### mywork todo
按 action 类型查询当前用户的工作项列表。无需 MQL 即可查询待办/已办。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --action | string | 是 | todo(待办)/done(已办)/overdue(逾期)/this_week(本周待办) |
| --page-num | number | 是 | 页码,从 1 开始,每页 50 条 |
| --asset-key | string | 否 | 工作区 key(格式 Asset_xxx),仅在报错需要选择时传 |
需完整结果时,从 page_num=1 连续翻页直到空为止。
---
## WorkHour 工时域
> 工时记录查询(`workhour list-records`)的参数表见 [references/misc.md](references/misc.md)。
### workhour list-schedule
获取指定人员在时间区间内的排期与工作量明细。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --project-key | string | 是 | 空间 key |
| --user-keys | array | 是 | 用户标识(名称/邮箱/userkey),**每次最多 20 个** |
| --start-time | string | 是 | 开始时间,格式 YYYY-MM-DD |
| --end-time | string | 是 | 结束时间,格式 YYYY-MM-DD,**单次跨度最大 3 个月** |
| --work-item-type-keys | array | 否 | 工作项类型列表,查询所有传入 `_all` |
**调用约束**:每次最多 20 人(多人拆批次并行);单次跨度 ≤ 3 个月(超出按月拆分);所有批次完成后再汇总,未完整获取前不得输出结论。
---
## UserGroup 人员域
> 团队相关命令(`team list` / `team list-members`)的参数表见 [references/misc.md](references/misc.md)。
### user search
批量查询用户基础信息。用于将姓名/邮箱转换为 userkey。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --user-keys | array | 是 | userKey、Email 或名字,最多 20 个 |
| --project-key | string | 否 | 空间 key |
| --need-all-status | boolean | 否 | 是否返回所有状态用户;默认 false,仅返回在职用户 |
### user me
查看当前用户信息。无需参数。
> **MQL 中**可直接用 `current_login_user()` 函数,无需提前获取用户信息。如需获取当前用户的 userkey/姓名等详细信息,用 `user me`。
---
## View 视图域
> 视图搜索与固定视图管理(`view search` / `view create-fixed` / `view update-fixed`)的参数表见 [references/view.md](references/view.md)。
### view get
根据视图 ID 获取该视图下的工作项列表。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --view-id | string | 是 | 视图 ID |
| --project-key | string | 否 | 空间 key |
| --page-num | number | 否 | 分页页数起点 |
| --fields | array | 否 | 要查询的字段 |
---
## Comment 评论域
> 评论列表查询(`comment list`)的参数表见 [references/misc.md](references/misc.md)。
### comment add
添加评论。支持富文本 Markdown,语法详见 [references/rich-text-editor-markdown-syntax.md](references/rich-text-editor-markdown-syntax.md)(含 @提及、对齐、链接预览、字号/颜色等扩展语法)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| --work-item-id | string | 是 | 工作项 ID |
| --content | string | 是 | 评论内容 |
---
## Deliverable 交付物域
> 单命令小域,参数表见 [references/misc.md](references/misc.md)。
### deliverable list
查看交付物详情及其根工作项 / 来源工作项。可按工作项 ID 列表过滤。
---
## Resource 资源库
> 资源库(资源模板)管理。`resource create` 当前对应 MCP `create_resource_work_item`,用于创建资源实例;查看资源库的字段 / 角色配置用 `resource meta-fields`。详细参数表见 [references/misc.md](references/misc.md)。
### resource create
在已启用资源库的工作项类型下创建资源模板(资源实例)。创建前先调 `resource meta-fields` 取字段 / 角色配置。
**语义边界**:
- 创建资源实例:`work_item_type_key` 是资源库启用的工作项类型;`template_id` 是该类型下的流程模板 ID/名称;`fields` / `roles` 是新资源实例自身的字段和角色。
- 从资源实例创建普通工作项:不要把已有资源实例 ID 填到 `work_item_type_key` 或 `template_id`。必须以当前 `resource create` 的 inspect/schema 为准确认是否有源资源实例参数;若当前 schema 未暴露该参数,先向用户说明无法确认自动化参数,不要猜。
---
## WBS 计划表
> 计划表(WBS)有 **草稿(draft)** 与 **已发布实例(instance)** 两套数据模型。常见编辑流程:`wbs create-draft` → 多次 `wbs edit-draft` → `wbs publish-draft`;放弃改动用 `wbs reset-draft`。详细参数表与 `wbs edit-draft` 的 operation 子类型见 [references/wbs.md](references/wbs.md)。
### wbs list-draft-rows
在计划表草稿中按条件筛选行。常用筛选字段:`wbs_name`、`wbs_parent_id`、`wbs_owner_in_charge`、`wbs_states_doing`。详见 [references/wbs.md](references/wbs.md)。
### wbs list-instance-rows
在已发布的线上计划表实例中按条件筛选行。参数同 `wbs list-draft-rows`。
### wbs edit-draft
对计划表草稿执行一次原子编辑。一次调用只能传一个 `operation_type`,但该操作内部可通过 `items` / `uuids` 等数组承载单行或批量编辑;支持新增 / 删除 / 恢复 / 排序 / 改名 / 改负责人 / 改阶段 / 改排期 / 改估分 / 改实际工时等,结构见 [references/wbs.md](references/wbs.md)。
> ⚠️ **前置**:草稿不存在时先调 `wbs create-draft`,再调 `wbs edit-draft`。判断方法:直接 `wbs list-draft-rows` 报"草稿不存在"类错误即视为缺失草稿。
### wbs publish-draft
将编辑完成的草稿发布到线上。
> ⚠️ 全量发布前必须用**固定话术**二次确认:"本人及协同者的全部编辑内容均会被发布,请确认是否全量发布?";部分发布(传入 `uuid_strings_list`)无需二次确认。
---
## 其它低频域
度量图表、子任务、关系定义查询的命令参数表见 [references/misc.md](references/misc.md):
- **Chart 度量域** — `chart get` / `chart list`
- **SubTask 子任务域** — `subtask update`(create/update/confirm/rollback)
- **Relation 关系域** — `relation list` / `relation meta-definitions`
- **WBS 计划表 · 辅助命令** — `wbs create-draft` / `wbs reset-draft` / `wbs get-draft-progress` / `wbs list-element-templates`(见 [references/wbs.md](references/wbs.md))
---
## 字段值格式(field_value)
> 🚨 **STRING 协议**:`field_value` 协议层固定为字符串。标量(text/number/bool/option_id/userkey/毫秒)直接作字符串;数组、对象**必须先 JSON.stringify** 再传,直接传会报 `need STRING type, but got: LIST` / `MAP`。
> 例:multi-user 正确写法为 `"[\"<userkey>\"]"`,错误写法为 `["<userkey>"]`。
| 字段类型 | 语义 | field_value 传参(已按前述规则序列化) |
|---------|------|------|
| template | 模板 ID(**创建必填**) | `"<template_id>"` — 用 `workitem meta-fields(field_keys=["template"])` 获取 |
| text / multi-pure-text / link / bool / number | 单个字面值 | `"需求标题"` / `"100"` / `"true"` |
| user | 单个 userkey | `"<userkey>"` |
| multi-user | userkey 数组(**stringified**) | `"[\"<userkey1>\",\"<userkey2>\"]"` |
| select / radio / tree-select | 枚举项 option_id | `"<option_id>"` |
| multi-select | option_id 对象数组(**stringified**) | `"[{\"option_id\":\"111\"},{\"option_id\":\"222\"}]"` |
| tree-multi-select | option_id 字符串数组(**stringified**) | `"[\"id1\",\"id2\"]"` |
| multi-text | 富文本 Markdown 字符串(语法详见 [references/rich-text-editor-markdown-syntax.md](references/rich-text-editor-markdown-syntax.md)) | `"**加粗**内容"` |
| date | 毫秒时间戳(天精度) | `"1722182400000"` |
| schedule | `[开始ms, 结束ms]`(**stringified**) | `"[1722182400000,1722355199999]"` |
| precise_date | 对象(**stringified**) | `"{\"start_time\":1722182400000,\"end_time\":1722355199999}"` |
| workitem_related_select | 关联工作项 ID | `"<work_item_id>"` |
| workitem_related_multi_select | ID 数组(**stringified**,数字元素) | `"[<id1>,<id2>]"` |
| role_owners(仅创建时) | 角色-人员对象数组(**stringified**) | `"[{\"role\":\"<role_id>\",\"owners\":[\"<userkey>\"]}]"` |
| signal | option_id 字符串(**写入值位**;MQL 查询值位为 label,见 [mql-syntax.md §4](references/mql-syntax.md)) | `"<option_id>"`(以 `workitem meta-fields` 的 `options[].option_id` 为准;当前系统外信号常见 4 个 option:`passed` / `notpassed` / `processing` / `noinformationyet`) |
| compound_field | 普通复合明细表(**stringified** action 对象) | `"{\"action\":\"add\",\"fields\":[[{\"field_key\":\"sub_key1\",\"field_value\":\"v1\"}]]}"` |
| multi_user_compound_field | 多人复合明细表(**仅更新已有人员**;stringified userkey map,整体覆盖) | `"{\"userkey1\":[{\"field_key\":\"sub_key1\",\"field_value\":\"v1\"}],\"userkey2\":[]}"` |
> 更新角色时不用 fields,用 `workitem update` 的 `role_operate` 参数。
### 普通复合明细表(compound_field)
普通复合明细表通过 `workitem update` 写入时,`field_value` 是 **stringified JSON action 对象**。直接传 JSON object 会在客户端序列化阶段报 `unsupported type: map[string]interface {}, expected type: STRING`。
```json
{"action": "add", "fields": [[{"field_key": "子字段key", "field_value": "子字段值"}, ...]]}
```
- **action** — 操作类型:`"add"`(新增行)、`"update"`(更新行)、`"delete"`(删除行)
- **fields** — **二维数组**:外层每个元素代表一行记录,内层是该行的子字段列表
- 子字段的 `field_value` 遵循各自字段类型的 STRING 协议(text 传纯字符串,multi-user 传 stringified 数组等)
- 子字段 key 从 `workitem meta-fields` 返回的 `compound_field_info` 中获取
- `add` 不传行标识;读取新增结果后,每行会带 `group_uuid`
- `update` / `delete` 必须原样传 `group_uuid` 定位行,键名就是 `group_uuid`,**不是 `record_id`**
更新一行:
```json
{"action": "update", "group_uuid": "读回的组标识", "fields": [[{"field_key": "子字段key", "field_value": "新值"}]]}
```
删除一行:
```json
{"action": "delete", "group_uuid": "读回的组标识"}
```
### 多人复合明细表(multi_user_compound_field)
多人复合明细表**不使用 action / group_uuid 协议**。写入值是 **stringified JSON map**:key 为填写人的 userkey,value 为该人员的子字段数组。
```json
{
"userkey1": [
{"field_key": "子字段key", "field_value": "子字段值"}
],
"userkey2": []
}
```
🚨 **整体覆盖,非增量更新**。仅传部分 userkey 会导致未包含的 userkey 整行被删除。更新前必须:
1. 用 `workitem get` 读取当前多人复合字段;返回 map 的 key 是当前人员范围,每个 value 含 `user`,有值时另含 `child_field_list`
2. 用 `workitem meta-fields` 读取 `compound_field_info`,确定子字段 key、类型和枚举 option_id
3. 重建**包含全部现有 userkey** 的 map;非目标人员的子字段值也要保留,只修改目标人员
4. 将完整 map JSON.stringify 后写回;写后再次读取核对所有人员和目标子字段
此协议只用于修改 `workitem get` 当前 map 中**已经存在的人员**。元数据接口只返回子字段配置;`workitem meta-roles` 只返回角色字典,二者均不返回 `editable_personnel_range_type`、字段绑定角色、可选人员或新增人员协议。若当前值为空或目标人员不在 map 中,立即停止并说明当前 Skill / CLI 无法自动新增人员;由用户先通过页面把人员加入范围,再重新读取后更新。不要尝试用 `{"userkey":[]}` 新增人员——接口会返回成功但回读仍为空。
枚举子字段仍传 option_id;读取返回的 `{label, value}` 不能原样回写,应取其中的 option_id 值。修改接口返回空成功不代表已落值,必须回读;若人员或目标值未变化,按未生效报告,不得宣称成功。
### 关联工作项字段(workitem_related_*)
用户提供名称而非 ID 时,需按名称→ID 转换流程(搜目标空间+类型,消歧,写入格式,防循环引用):详见 [references/field-value-extras.md](references/field-value-extras.md)。
---
## 常用场景速查
| 场景 | 命令(注意点) |
|------|-------|
| 空间名 → project_key | `project search` |
| 查类型 / 字段 / 角色 | `workitem meta-types` / `workitem meta-fields` / `workitem meta-roles` |
| 人名 → userkey | `user search`(批量 ≤20) |
| 当前用户 | `user me`;MQL 内可直接 `current_login_user()` |
| 条件查询 / 个人待办 | `workitem query`(MQL) / `mywork todo` |
| 团队排期 | `workhour list-schedule`(≤20 人、≤3 月) |
| 创建 / 修改工作项 | `workitem create` / `workitem update`(字段 fields,角色 role_operate) |
| 节点流转 / 状态流转 | `workflow transition`(confirm/rollback) / `workflow transition-state`(先 `workflow list-state-transitions`) |
| 视图数据 | `view get` |
| 生成 Meegle 页面链接 | 仅在用户明确要求链接或链接本身是交付物时,读取 [references/url-links.md](references/url-links.md) |
## 链接生成
仅当用户明确要求“给链接 / 拼链接 / 打开地址”,或链接本身是请求的交付物时,读取并执行 [references/url-links.md](references/url-links.md)。不要给普通查询、创建或更新结果自动附加链接。
## 通用规范
### 请求处理流程
收到用户输入后依次执行:
1. **参数提取**:从自然语言中提取空间名、工作项类型、时间、人员、筛选条件;含 URL 时先调 `url decode` 解析,按 [references/url-kinds.md](references/url-kinds.md) 的 `url_kind` 分支决定进入哪个 SOP 或拒绝。**禁止**自己从 URL 截取路径段作参数。注意区分空间名与筛选维度(如「XX空间下YY业务线的缺陷」中 XX 才是空间名)。
2. **参数确认**(禁止猜测):用探测命令校验空间(`project search`)、类型(`workitem meta-types`)、人员(`user search`)。**探测结果不唯一时必须展示并询问用户**,禁止自行选择;缺失必填合并为一条消息询问。个人待办(`mywork todo`)可跳过;URL 经 `url decode` 拿到 `simple_name` 后仍需 `project search` 转权威 `project_key`(同名空间可能有多个无权限)。
3. **元数据收集**(无需用户参与):调用 `workitem meta-fields` 获取字段定义(需要特定字段用 `field_keys`,模糊查询用 `field_query`)。**必须同时传 `--project-key` 与 `--work-item-type`**;缺一都会拿到错误 / 空 / 跨类型污染的字段配置。涉及角色时并行调 `workitem meta-roles`。关键字段识别:状态字段 type=`_work_item_status`(含「完成/关闭/终止」的值为完成态)、排期字段 type=`schedule`(MQL 用 `__字段名_开始时间` / `__字段名_结束时间`)、优先级字段 key=`priority`。简单直调场景(仅需 project_key + work_item_id,如 `comment add`)可跳过本步。
4. **执行**:调用目标命令,遵循 [references/performance.md](references/performance.md) 的并行/翻页规则。
5. **判断是否续处理**:完成 CLI 可执行的部分,或确认用户请求超出 CLI 能力后,按 [AI 助手续处理细则](references/ai-handoff.md) 判断是否提供兜底跳转链接。链接只补充 CLI 无法完成的部分,不能替代已能返回的数据或操作结果。
### 并行与大结果
详见 [references/performance.md](references/performance.md):并行调用(必须串行的链路、可并行的组合)、大结果分批与翻页规则。
### 错误处理
**总则**:失败后从返回的 `err_msg` / `inner_err` 中提取错误原因,针对性修正后重试;**最多自动重试 2 次**,连续 3 次同类失败后停止并向用户说明。
**熔断条件**(立即终止,禁止盲目重试):
- 空间未找到(`project search` 连续 3 次失败)
- Permission Denied(当前用户对该空间无访问权限)
- `ai-handoff availability` 返回不可用,或 `ai-handoff create-link` 返回 `HANDOFF_REJECTED` / `LOCAL_DISABLED`;不得为生成链接而重复调用
详细自愈规则与错误速查表(涵盖字段格式、节点流转、人员转换等常见报错)见 [references/error-handling.md](references/error-handling.md)。
---
## AI 助手续处理
仅当用户还需要分析、总结、诊断、建议,或请求了 CLI 不支持的操作时,读取并执行 [references/ai-handoff.md](references/ai-handoff.md)。核心约束:
- 先交付 CLI 能完成的结果;若请求本身完全不受 CLI 支持,明确能力边界后可直接进入续处理判断,不得伪造一次 CLI 输出;
- `available=false` 或 `mode=off` 时不生成链接;`mode=auto` 可直接生成,`mode=ask` 必须先征得用户同意;
- 每轮最多生成一条链接,同一会话最多主动引导一次;链接只携带 query 与 ID 指针,不声称已传递 CLI 完整结果集。
---
## 操作指南(SOP)
具体操作的完整流程、字段转换和自愈机制见对应 SOP:
- [创建工作项](references/sop-create-workitem.md) — 创建需求、任务、缺陷
- [更新工作项](references/sop-update-workitem.md) — 修改字段、更新角色、追加内容
- [流转节点(节点流)](references/sop-transition-node.md) — 完成/回滚节点、批量流转
- [流转状态(状态流)](references/sop-transition-state.md) — 流转缺陷/issue、关闭 bug