fix: harden dws contract edge cases
This commit is contained in:
@@ -9377,7 +9377,7 @@
|
||||
]
|
||||
},
|
||||
"doc info": {
|
||||
"agent_summary": "获取文档元信息(标题/类型/创建者/权限等)",
|
||||
"agent_summary": "获取文档元信息(标题/类型/创建者/权限等,并从钉盘补齐缺失的文件大小)",
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
@@ -9386,21 +9386,21 @@
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
"effect_source": "command-verb",
|
||||
"effect_source": "agent-hint",
|
||||
"examples": [
|
||||
"dws doc info --node \u003cDOC_ID\u003e --format json",
|
||||
"dws doc info --node \"https://alidocs.dingtalk.com/i/nodes/\u003cDOC_UUID\u003e\" --format json"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "获取文档元信息(标题/类型/创建者/权限等)",
|
||||
"value": "获取文档元信息(标题/类型/创建者/权限等,并从钉盘补齐缺失的文件大小)",
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "获取文档元信息(标题/类型/创建者/权限等)",
|
||||
"value": "获取文档元信息(标题/类型/创建者/权限等,并从钉盘补齐缺失的文件大小)",
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -9413,14 +9413,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/doc marks this tool as reviewed",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/doc marks this tool as reviewed"
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -9454,29 +9454,45 @@
|
||||
},
|
||||
"confirmation": {
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
},
|
||||
{
|
||||
"value": "not_required",
|
||||
"source": "risk-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"effect": {
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "read",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
},
|
||||
{
|
||||
"value": "read",
|
||||
"source": "command-verb",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -9504,31 +9520,39 @@
|
||||
},
|
||||
"idempotency": {
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
},
|
||||
{
|
||||
"value": "idempotent",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_mode": {
|
||||
"value": "mcp",
|
||||
"value": "composite",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/doc marks this tool as reviewed",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"value": "composite",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/doc marks this tool as reviewed"
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -9538,17 +9562,43 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": "doc.get_document_info",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "imported",
|
||||
"interface_reason": {
|
||||
"value": "The CLI uses doc/get_document_info as the authoritative response and best-effort enriches a missing fileSize from drive/get_file_info, so no single MCP interface represents the delivered result.",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "The CLI uses doc/get_document_info as the authoritative response and best-effort enriches a missing fileSize from drive/get_file_info, so no single MCP interface represents the delivered result.",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
}
|
||||
]
|
||||
},
|
||||
"interface_ref": {
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "interface_disposition_matrix",
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref",
|
||||
"candidates": [
|
||||
{
|
||||
"value": null,
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "final interface mode composite forbids a direct MCP interface_ref"
|
||||
}
|
||||
],
|
||||
"overridden_candidates": [
|
||||
{
|
||||
"value": "doc.get_document_info",
|
||||
"source": "internal/cli/schema_hints/imported/wukong.json",
|
||||
"precedence": "imported",
|
||||
"selected": true
|
||||
"selected": false
|
||||
},
|
||||
{
|
||||
"value": "doc.get_document_info",
|
||||
@@ -9563,14 +9613,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/doc marks this tool as reviewed",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/doc marks this tool as reviewed"
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
@@ -9583,22 +9633,31 @@
|
||||
},
|
||||
"risk": {
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "low",
|
||||
"source": "internal/cli/schema_hints/metadata/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields."
|
||||
},
|
||||
{
|
||||
"value": "low",
|
||||
"source": "effect-default",
|
||||
"precedence": "inference_or_default",
|
||||
"selected": true
|
||||
"selected": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户要查看文档/节点元信息(标题、类型、创建者、权限)时",
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时"
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时",
|
||||
"文档接口本身缺少 fileSize,但用户需要读取上传文件大小时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -9608,7 +9667,8 @@
|
||||
{
|
||||
"value": [
|
||||
"用户要查看文档/节点元信息(标题、类型、创建者、权限)时",
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时"
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时",
|
||||
"文档接口本身缺少 fileSize,但用户需要读取上传文件大小时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -9619,11 +9679,8 @@
|
||||
}
|
||||
},
|
||||
"idempotency": "idempotent",
|
||||
"interface_mode": "mcp",
|
||||
"interface_ref": {
|
||||
"product_id": "doc",
|
||||
"rpc_name": "get_document_info"
|
||||
},
|
||||
"interface_mode": "composite",
|
||||
"interface_reason": "The CLI uses doc/get_document_info as the authoritative response and best-effort enriches a missing fileSize from drive/get_file_info, so no single MCP interface represents the delivered result.",
|
||||
"reviewed": true,
|
||||
"risk": "low",
|
||||
"source_refs": [
|
||||
@@ -9645,7 +9702,8 @@
|
||||
],
|
||||
"use_when": [
|
||||
"用户要查看文档/节点元信息(标题、类型、创建者、权限)时",
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时"
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时",
|
||||
"文档接口本身缺少 fileSize,但用户需要读取上传文件大小时"
|
||||
]
|
||||
},
|
||||
"doc list": {
|
||||
@@ -12274,7 +12332,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时"
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -12283,7 +12341,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时"
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -12315,7 +12373,7 @@
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#doc.rename_document"
|
||||
],
|
||||
"use_when": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时"
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
]
|
||||
},
|
||||
"doc search": {
|
||||
|
||||
@@ -7083,7 +7083,7 @@
|
||||
]
|
||||
},
|
||||
"drive rename": {
|
||||
"agent_summary": "修改文档空间中文档或文件的名称",
|
||||
"agent_summary": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"agent_summary_source": "dws-agent-selection/drive",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
@@ -7098,14 +7098,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "修改文档空间中文档或文件的名称",
|
||||
"value": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "修改文档空间中文档或文件的名称",
|
||||
"value": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -7118,14 +7118,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -7224,14 +7224,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -7246,14 +7246,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "doc.rename_document",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
},
|
||||
{
|
||||
"value": "doc.rename_document",
|
||||
@@ -7268,14 +7268,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/drive marks this tool as reviewed"
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
@@ -7302,7 +7302,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时"
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -7311,7 +7311,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时"
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -7342,7 +7342,7 @@
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#drive.rename_document"
|
||||
],
|
||||
"use_when": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时"
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
]
|
||||
},
|
||||
"drive search": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:557f4019cc83df15cbfe6d1611774331b0bdde87c0b5748c4d37d778b35abade",
|
||||
"source_hash": "sha256:920c33b1853777cdb21a14d6e44ba75d3e0497f3470b3f04f10fa0a3f8715cb5",
|
||||
"surface_hash": "sha256:4cf8460240b19f896c3a330c69982cbb5f57aa8576cdf30373172082eea893ed",
|
||||
"coverage": {
|
||||
"surface_products": 26,
|
||||
|
||||
@@ -5829,12 +5829,13 @@
|
||||
]
|
||||
},
|
||||
"todo task add-reminder": {
|
||||
"agent_summary": "添加待办提醒",
|
||||
"agent_summary": "写入一条待办提醒规则(上游不支持规则读回)",
|
||||
"agent_summary_source": "dws-agent-selection/todo",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"要重置/清除提醒规则时改用 dws todo task reset-reminder",
|
||||
"提醒时间与目标待办未确认时不要添加"
|
||||
"提醒时间与目标待办未确认时不要添加",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -5845,14 +5846,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "添加待办提醒",
|
||||
"value": "写入一条待办提醒规则(上游不支持规则读回)",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "结合本机 dws schema 实时描述、Skill 与 Cobra 手写选型;pinned schema_mcp_metadata 仅对照。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "添加待办提醒",
|
||||
"value": "写入一条待办提醒规则(上游不支持规则读回)",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -5885,7 +5886,8 @@
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"要重置/清除提醒规则时改用 dws todo task reset-reminder",
|
||||
"提醒时间与目标待办未确认时不要添加"
|
||||
"提醒时间与目标待办未确认时不要添加",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -5895,7 +5897,8 @@
|
||||
{
|
||||
"value": [
|
||||
"要重置/清除提醒规则时改用 dws todo task reset-reminder",
|
||||
"提醒时间与目标待办未确认时不要添加"
|
||||
"提醒时间与目标待办未确认时不要添加",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -6043,7 +6046,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间)"
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间);成功响应仅作为写入回执"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -6052,7 +6055,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间)"
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间);成功响应仅作为写入回执"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -6078,11 +6081,12 @@
|
||||
"internal/cli/schema_hints/selection/todo.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.todo.add_todo_reminder",
|
||||
"live-dws-schema:todo.add_todo_reminder",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/todo.md",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#todo.add_todo_reminder"
|
||||
],
|
||||
"use_when": [
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间)"
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间);成功响应仅作为写入回执"
|
||||
]
|
||||
},
|
||||
"todo task create": {
|
||||
@@ -6343,6 +6347,7 @@
|
||||
"internal/cli/schema_hints/selection/todo.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.todo.create_personal_todo",
|
||||
"live-dws-schema:todo.create_personal_todo",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/todo.md",
|
||||
"structured-hint:internal/cli/schema_hints/imported/wukong.json#todo.create_personal_todo",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#todo.create_personal_todo"
|
||||
@@ -7183,7 +7188,8 @@
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要按条件查多个待办时改用 dws todo task list",
|
||||
"需要修改待办时改用 update/done/delete 等写命令"
|
||||
"需要修改待办时改用 update/done/delete 等写命令",
|
||||
"需要读取或验证提醒规则时不要使用;当前详情接口不返回 reminderRules"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -7234,7 +7240,8 @@
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要按条件查多个待办时改用 dws todo task list",
|
||||
"需要修改待办时改用 update/done/delete 等写命令"
|
||||
"需要修改待办时改用 update/done/delete 等写命令",
|
||||
"需要读取或验证提醒规则时不要使用;当前详情接口不返回 reminderRules"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -7244,7 +7251,8 @@
|
||||
{
|
||||
"value": [
|
||||
"需要按条件查多个待办时改用 dws todo task list",
|
||||
"需要修改待办时改用 update/done/delete 等写命令"
|
||||
"需要修改待办时改用 update/done/delete 等写命令",
|
||||
"需要读取或验证提醒规则时不要使用;当前详情接口不返回 reminderRules"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -8559,12 +8567,13 @@
|
||||
]
|
||||
},
|
||||
"todo task reset-reminder": {
|
||||
"agent_summary": "重置待办提醒",
|
||||
"agent_summary": "整体替换或清除待办提醒规则(上游不支持规则读回)",
|
||||
"agent_summary_source": "dws-agent-selection/todo",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只需追加一条提醒时改用 dws todo task add-reminder",
|
||||
"新规则与目标待办未确认时不要重置"
|
||||
"新规则与目标待办未确认时不要重置",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -8575,14 +8584,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "重置待办提醒",
|
||||
"value": "整体替换或清除待办提醒规则(上游不支持规则读回)",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "结合本机 dws schema 实时描述、Skill 与 Cobra 手写选型;pinned schema_mcp_metadata 仅对照。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "重置待办提醒",
|
||||
"value": "整体替换或清除待办提醒规则(上游不支持规则读回)",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -8615,7 +8624,8 @@
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"只需追加一条提醒时改用 dws todo task add-reminder",
|
||||
"新规则与目标待办未确认时不要重置"
|
||||
"新规则与目标待办未确认时不要重置",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -8625,7 +8635,8 @@
|
||||
{
|
||||
"value": [
|
||||
"只需追加一条提醒时改用 dws todo task add-reminder",
|
||||
"新规则与目标待办未确认时不要重置"
|
||||
"新规则与目标待办未确认时不要重置",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -8773,7 +8784,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除)"
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -8782,7 +8793,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除)"
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -8812,7 +8823,7 @@
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#todo.reset_todo_reminder"
|
||||
],
|
||||
"use_when": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除)"
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
]
|
||||
},
|
||||
"todo task update": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:557f4019cc83df15cbfe6d1611774331b0bdde87c0b5748c4d37d778b35abade",
|
||||
"source_hash": "sha256:920c33b1853777cdb21a14d6e44ba75d3e0497f3470b3f04f10fa0a3f8715cb5",
|
||||
"surface_hash": "sha256:4cf8460240b19f896c3a330c69982cbb5f57aa8576cdf30373172082eea893ed",
|
||||
"source_files": 160,
|
||||
"hint_files": 54,
|
||||
|
||||
+221
-141
File diff suppressed because it is too large
Load Diff
@@ -144,9 +144,16 @@
|
||||
"runtime_gate": "none"
|
||||
},
|
||||
"doc.get_document_info": {
|
||||
"interface_mode": "mcp",
|
||||
"effect": "read",
|
||||
"risk": "low",
|
||||
"confirmation": "not_required",
|
||||
"idempotency": "idempotent",
|
||||
"interface_mode": "composite",
|
||||
"availability": "available",
|
||||
"reviewed": true
|
||||
"interface_reason": "The CLI uses doc/get_document_info as the authoritative response and best-effort enriches a missing fileSize from drive/get_file_info, so no single MCP interface represents the delivered result.",
|
||||
"reviewed": true,
|
||||
"review_reason": "Reviewed composite metadata read fixes the document endpoint's missing fileSize without changing its authoritative title, type, owner, or permission fields.",
|
||||
"runtime_gate": "none"
|
||||
},
|
||||
"doc.import_get": {
|
||||
"effect": "write",
|
||||
|
||||
@@ -203,7 +203,14 @@
|
||||
},
|
||||
"interface_mode": "mcp",
|
||||
"availability": "available",
|
||||
"reviewed": true
|
||||
"reviewed": true,
|
||||
"parameters": {
|
||||
"name": {
|
||||
"description": "新显示名称;服务端保留节点原扩展名,若传入常见文件扩展名,CLI 会自动去掉一层以避免双扩展名"
|
||||
}
|
||||
},
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"cli_path": "drive rename"
|
||||
},
|
||||
"drive.search_files": {
|
||||
"interface_mode": "mcp",
|
||||
|
||||
@@ -321,10 +321,11 @@
|
||||
]
|
||||
},
|
||||
"doc.get_document_info": {
|
||||
"agent_summary": "获取文档元信息(标题/类型/创建者/权限等)",
|
||||
"agent_summary": "获取文档元信息(标题/类型/创建者/权限等,并从钉盘补齐缺失的文件大小)",
|
||||
"use_when": [
|
||||
"用户要查看文档/节点元信息(标题、类型、创建者、权限)时",
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时"
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时",
|
||||
"文档接口本身缺少 fileSize,但用户需要读取上传文件大小时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"已确认是 adoc 且只要正文改用 dws doc read",
|
||||
@@ -581,7 +582,7 @@
|
||||
"doc.rename_document": {
|
||||
"agent_summary": "兼容入口:重命名文档或文件;文件重命名能力已迁移到 drive。",
|
||||
"use_when": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时"
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
],
|
||||
"avoid_when": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
|
||||
@@ -545,9 +545,9 @@
|
||||
]
|
||||
},
|
||||
"drive.rename_document": {
|
||||
"agent_summary": "修改文档空间中文档或文件的名称",
|
||||
"agent_summary": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"use_when": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时"
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
],
|
||||
"avoid_when": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
|
||||
@@ -103,13 +103,14 @@
|
||||
]
|
||||
},
|
||||
"todo.add_todo_reminder": {
|
||||
"agent_summary": "添加待办提醒",
|
||||
"agent_summary": "写入一条待办提醒规则(上游不支持规则读回)",
|
||||
"use_when": [
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间)"
|
||||
"已知 taskId,需要按截止时间偏移或自定义时间戳添加提醒时(dueTime 模式要求待办已有截止时间);成功响应仅作为写入回执"
|
||||
],
|
||||
"avoid_when": [
|
||||
"要重置/清除提醒规则时改用 dws todo task reset-reminder",
|
||||
"提醒时间与目标待办未确认时不要添加"
|
||||
"提醒时间与目标待办未确认时不要添加",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"examples": [
|
||||
"dws todo task add-reminder --task-id <taskId> --base-time dueTime --due-date-offset -30",
|
||||
@@ -224,7 +225,8 @@
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要按条件查多个待办时改用 dws todo task list",
|
||||
"需要修改待办时改用 update/done/delete 等写命令"
|
||||
"需要修改待办时改用 update/done/delete 等写命令",
|
||||
"需要读取或验证提醒规则时不要使用;当前详情接口不返回 reminderRules"
|
||||
],
|
||||
"examples": [
|
||||
"dws todo task get --task-id <taskId>",
|
||||
@@ -356,13 +358,14 @@
|
||||
]
|
||||
},
|
||||
"todo.reset_todo_reminder": {
|
||||
"agent_summary": "重置待办提醒",
|
||||
"agent_summary": "整体替换或清除待办提醒规则(上游不支持规则读回)",
|
||||
"use_when": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除)"
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
],
|
||||
"avoid_when": [
|
||||
"只需追加一条提醒时改用 dws todo task add-reminder",
|
||||
"新规则与目标待办未确认时不要重置"
|
||||
"新规则与目标待办未确认时不要重置",
|
||||
"业务要求写后读取并核验 reminderRules 时不要声称可验证;当前 task get/list 均不返回提醒规则"
|
||||
],
|
||||
"examples": [
|
||||
"dws todo task reset-reminder --task-id <taskId>",
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/output"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -49,3 +52,26 @@ func commandBoolFlag(cmd *cobra.Command, name string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// renameBaseName removes one familiar file extension before calling
|
||||
// rename_document. That service preserves the node's current extension, so
|
||||
// forwarding "report.txt" for a .txt file would otherwise produce
|
||||
// "report.txt.txt". Unknown dotted suffixes are preserved because they may be
|
||||
// part of a display name (for example "release.v2").
|
||||
func renameBaseName(name string) string {
|
||||
trimmed := strings.TrimSpace(name)
|
||||
extension := strings.ToLower(strings.TrimPrefix(filepath.Ext(trimmed), "."))
|
||||
if _, ok := renamePreservedExtensions[extension]; !ok {
|
||||
return trimmed
|
||||
}
|
||||
return strings.TrimSuffix(trimmed, filepath.Ext(trimmed))
|
||||
}
|
||||
|
||||
var renamePreservedExtensions = map[string]struct{}{
|
||||
"7z": {}, "able": {}, "adoc": {}, "adraw": {}, "aform": {}, "amind": {},
|
||||
"appt": {}, "awbd": {}, "axls": {}, "csv": {}, "doc": {}, "docx": {},
|
||||
"gif": {}, "gz": {}, "jpeg": {}, "jpg": {}, "json": {}, "md": {},
|
||||
"mp3": {}, "mp4": {}, "pdf": {}, "png": {}, "ppt": {}, "pptx": {},
|
||||
"rar": {}, "svg": {}, "tar": {}, "txt": {}, "wav": {}, "webp": {},
|
||||
"xls": {}, "xlsx": {}, "xml": {}, "zip": {},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type contractDefectCaller struct {
|
||||
dryRun bool
|
||||
calls []guardedMutationCall
|
||||
responses map[string]string
|
||||
errors map[string]error
|
||||
}
|
||||
|
||||
func (c *contractDefectCaller) CallTool(_ context.Context, productID, toolName string, args map[string]any) (*edition.ToolResult, error) {
|
||||
c.calls = append(c.calls, guardedMutationCall{productID: productID, toolName: toolName, args: args})
|
||||
key := productID + "/" + toolName
|
||||
if err := c.errors[key]; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
text := c.responses[key]
|
||||
if text == "" {
|
||||
text = `{}`
|
||||
}
|
||||
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: text}}}, nil
|
||||
}
|
||||
|
||||
func (*contractDefectCaller) Format() string { return "json" }
|
||||
func (c *contractDefectCaller) DryRun() bool { return c.dryRun }
|
||||
func (*contractDefectCaller) Fields() string { return "" }
|
||||
func (*contractDefectCaller) JQ() string { return "" }
|
||||
|
||||
func executeContractDefectCommand(t *testing.T, caller *contractDefectCaller, build func() *cobra.Command, args ...string) (string, error) {
|
||||
t.Helper()
|
||||
previousDeps := deps
|
||||
t.Cleanup(func() { deps = previousDeps })
|
||||
|
||||
InitDeps(caller)
|
||||
var stdout bytes.Buffer
|
||||
deps.Out.w = &stdout
|
||||
deps.Out.errW = io.Discard
|
||||
|
||||
root := build()
|
||||
if root.PersistentFlags().Lookup("yes") == nil {
|
||||
root.PersistentFlags().Bool("yes", false, "confirm high-risk operation")
|
||||
}
|
||||
if root.PersistentFlags().Lookup("dry-run") == nil {
|
||||
root.PersistentFlags().Bool("dry-run", false, "preview without executing")
|
||||
}
|
||||
root.SilenceErrors = true
|
||||
root.SilenceUsage = true
|
||||
root.SetArgs(args)
|
||||
err := root.Execute()
|
||||
return stdout.String(), err
|
||||
}
|
||||
|
||||
func TestApprovalRevokeDryRunSkipsConfirmationAndEmitsPreview(t *testing.T) {
|
||||
caller := &contractDefectCaller{dryRun: true}
|
||||
output, err := executeContractDefectCommand(t, caller, newOaCommand,
|
||||
"approval", "revoke", "--instance-id", "instance-dry-run", "--dry-run")
|
||||
if err != nil {
|
||||
t.Fatalf("approval revoke dry-run returned error: %v", err)
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("dry-run tool calls = %#v, want none", caller.calls)
|
||||
}
|
||||
if !strings.Contains(output, `"tool": "revoke_processInstance"`) ||
|
||||
!strings.Contains(output, `"processInstanceId": "instance-dry-run"`) {
|
||||
t.Fatalf("dry-run output = %q, want revoke preview", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocVersionRevertDryRunSkipsRemotePreflightAndEmitsPreview(t *testing.T) {
|
||||
caller := &contractDefectCaller{dryRun: true}
|
||||
output, err := executeContractDefectCommand(t, caller, newDocCommand,
|
||||
"version", "revert", "--node", "node-dry-run", "--version", "7", "--dry-run")
|
||||
if err != nil {
|
||||
t.Fatalf("doc version revert dry-run returned error: %v", err)
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("dry-run tool calls = %#v, want none", caller.calls)
|
||||
}
|
||||
if !strings.Contains(output, `"tool": "revert_doc_version"`) ||
|
||||
!strings.Contains(output, `"version": 7`) {
|
||||
t.Fatalf("dry-run output = %q, want version revert preview", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenameDocumentRemovesOnePreservedExtension(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
build func() *cobra.Command
|
||||
args []string
|
||||
}{
|
||||
{
|
||||
name: "doc",
|
||||
build: newDocCommand,
|
||||
args: []string{"rename", "--node", "node-1", "--name", "report.txt"},
|
||||
},
|
||||
{
|
||||
name: "drive",
|
||||
build: newDriveCommand,
|
||||
args: []string{"rename", "--node", "node-1", "--name", "report.txt"},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
caller := &contractDefectCaller{}
|
||||
if _, err := executeContractDefectCommand(t, caller, test.build, test.args...); err != nil {
|
||||
t.Fatalf("rename returned error: %v", err)
|
||||
}
|
||||
if len(caller.calls) != 1 {
|
||||
t.Fatalf("tool calls = %#v, want one rename call", caller.calls)
|
||||
}
|
||||
if got := caller.calls[0].args["newName"]; got != "report" {
|
||||
t.Fatalf("newName = %#v, want report", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if got := renameBaseName("release.v2"); got != "release.v2" {
|
||||
t.Fatalf("unknown dotted suffix = %q, want release.v2", got)
|
||||
}
|
||||
if got := renameBaseName("REPORT.XLSX"); got != "REPORT" {
|
||||
t.Fatalf("case-insensitive extension normalization = %q, want REPORT", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocInfoEnrichesMissingFileSizeFromDriveMetadata(t *testing.T) {
|
||||
caller := &contractDefectCaller{
|
||||
responses: map[string]string{
|
||||
"doc/get_document_info": `{"result":{"title":"budget.xlsx","fileSize":null}}`,
|
||||
"drive/get_file_info": `{"result":{"fileSize":93682}}`,
|
||||
},
|
||||
}
|
||||
output, err := executeContractDefectCommand(t, caller, newDocCommand,
|
||||
"info", "--node", "node-1")
|
||||
if err != nil {
|
||||
t.Fatalf("doc info returned error: %v", err)
|
||||
}
|
||||
wantCalls := []guardedMutationCall{
|
||||
{productID: "doc", toolName: "get_document_info", args: map[string]any{"nodeId": "node-1"}},
|
||||
{productID: "drive", toolName: "get_file_info", args: map[string]any{"fileId": "node-1"}},
|
||||
}
|
||||
if !reflect.DeepEqual(caller.calls, wantCalls) {
|
||||
t.Fatalf("tool calls = %#v, want %#v", caller.calls, wantCalls)
|
||||
}
|
||||
var payload struct {
|
||||
Result struct {
|
||||
FileSize int64 `json:"fileSize"`
|
||||
} `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(output), &payload); err != nil {
|
||||
t.Fatalf("decode doc info output %q: %v", output, err)
|
||||
}
|
||||
if payload.Result.FileSize != 93682 {
|
||||
t.Fatalf("fileSize = %d, want 93682", payload.Result.FileSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocInfoFallsBackWhenDriveMetadataIsUnavailable(t *testing.T) {
|
||||
caller := &contractDefectCaller{
|
||||
responses: map[string]string{
|
||||
"doc/get_document_info": `{"result":{"title":"online document"}}`,
|
||||
},
|
||||
errors: map[string]error{
|
||||
"drive/get_file_info": fmt.Errorf("drive metadata unavailable"),
|
||||
},
|
||||
}
|
||||
output, err := executeContractDefectCommand(t, caller, newDocCommand,
|
||||
"info", "--node", "node-1")
|
||||
if err != nil {
|
||||
t.Fatalf("doc info fallback returned error: %v", err)
|
||||
}
|
||||
if !strings.Contains(output, `"title": "online document"`) {
|
||||
t.Fatalf("fallback output = %q, want original document metadata", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocInfoDryRunUsesSingleStandardPreview(t *testing.T) {
|
||||
caller := &contractDefectCaller{dryRun: true}
|
||||
output, err := executeContractDefectCommand(t, caller, newDocCommand,
|
||||
"info", "--node", "node-dry-run", "--dry-run")
|
||||
if err != nil {
|
||||
t.Fatalf("doc info dry-run returned error: %v", err)
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("dry-run tool calls = %#v, want none", caller.calls)
|
||||
}
|
||||
if !strings.Contains(output, `"tool": "get_document_info"`) ||
|
||||
!strings.Contains(output, `"nodeId": "node-dry-run"`) {
|
||||
t.Fatalf("dry-run output = %q, want document info preview", output)
|
||||
}
|
||||
}
|
||||
+73
-15
@@ -61,6 +61,60 @@ func docVersionExists(ctx context.Context, nodeID string, version int) (bool, er
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// docInfoWithDriveSize keeps the document metadata response authoritative and
|
||||
// best-effort enriches its missing fileSize from drive/get_file_info. The
|
||||
// document metadata endpoint does not expose a size for several uploaded file
|
||||
// types even though the drive endpoint does.
|
||||
func docInfoWithDriveSize(ctx context.Context, nodeID string) error {
|
||||
docText, err := callMCPToolReturnTextOnServer(ctx, "doc", "get_document_info", map[string]any{
|
||||
"nodeId": nodeID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var docResponse map[string]any
|
||||
if err := json.Unmarshal([]byte(docText), &docResponse); err != nil {
|
||||
deps.Out.PrintRaw(docText)
|
||||
return nil
|
||||
}
|
||||
docResult, wrapped := docResponse["result"].(map[string]any)
|
||||
if !wrapped {
|
||||
docResult = docResponse
|
||||
}
|
||||
if current, exists := docResult["fileSize"]; exists && current != nil {
|
||||
return deps.Out.PrintJSON(docResponse)
|
||||
}
|
||||
|
||||
fileID := nodeID
|
||||
if extracted := extractNodeIDFromDocURL(nodeID); extracted != "" {
|
||||
fileID = extracted
|
||||
}
|
||||
driveText, err := callMCPToolReturnTextOnServer(ctx, "drive", "get_file_info", map[string]any{
|
||||
"fileId": fileID,
|
||||
})
|
||||
if err != nil {
|
||||
return deps.Out.PrintJSON(docResponse)
|
||||
}
|
||||
|
||||
var driveResponse map[string]any
|
||||
if err := json.Unmarshal([]byte(driveText), &driveResponse); err != nil {
|
||||
return deps.Out.PrintJSON(docResponse)
|
||||
}
|
||||
driveResult, ok := driveResponse["result"].(map[string]any)
|
||||
if !ok {
|
||||
driveResult = driveResponse
|
||||
}
|
||||
fileSize, exists := driveResult["fileSize"]
|
||||
if !exists || fileSize == nil {
|
||||
fileSize, exists = driveResult["size"]
|
||||
}
|
||||
if exists && fileSize != nil {
|
||||
docResult["fileSize"] = fileSize
|
||||
}
|
||||
return deps.Out.PrintJSON(docResponse)
|
||||
}
|
||||
|
||||
// docVersionNextCursor 从 list_doc_versions 响应中提取分页游标;没有下一页时返回 ""。
|
||||
func docVersionNextCursor(v any) string {
|
||||
switch val := v.(type) {
|
||||
@@ -917,7 +971,8 @@ func newDocCommand() *cobra.Command {
|
||||
infoCmd := &cobra.Command{
|
||||
Use: "info",
|
||||
Short: "获取文档元信息",
|
||||
Long: `获取文档标题、类型、创建者、创建时间、权限等元信息 (不含内容)。`,
|
||||
Long: `获取文档标题、类型、创建者、创建时间、权限等元信息 (不含内容)。
|
||||
文档接口未返回 fileSize 时,会从钉盘元数据补齐文件大小。`,
|
||||
Example: ` dws doc info --node DOC_ID
|
||||
dws doc info --node "https://alidocs.dingtalk.com/i/nodes/<DOC_UUID>"`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -925,9 +980,10 @@ func newDocCommand() *cobra.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return callMCPTool("get_document_info", map[string]any{
|
||||
"nodeId": nodeID,
|
||||
})
|
||||
if commandDryRun(cmd) {
|
||||
return callMCPToolOnServer("doc", "get_document_info", map[string]any{"nodeId": nodeID})
|
||||
}
|
||||
return docInfoWithDriveSize(cmd.Context(), nodeID)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1550,7 +1606,7 @@ WARNING: --mode overwrite 为破坏性写入,会清空原文档全部内容。
|
||||
}
|
||||
return callMCPTool("rename_document", map[string]any{
|
||||
"nodeId": nodeID,
|
||||
"newName": flagOrFallback(cmd, "name", "title"),
|
||||
"newName": renameBaseName(flagOrFallback(cmd, "name", "title")),
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -1740,7 +1796,7 @@ WARNING: --mode overwrite 为破坏性写入,会清空原文档全部内容。
|
||||
|
||||
// rename
|
||||
renameCmd.Flags().String("node", "", "文档/文件 ID 或 URL (必填)")
|
||||
renameCmd.Flags().String("name", "", "新名称 (必填)")
|
||||
renameCmd.Flags().String("name", "", "新名称 (必填;服务端保留原扩展名,传入常见扩展名时 CLI 会自动去掉一层)")
|
||||
|
||||
// delete
|
||||
deleteCmd.Flags().String("node", "", "文档/文件 ID 或 URL (必填)")
|
||||
@@ -2709,15 +2765,17 @@ CLI 内部自动完成全部流程:
|
||||
return fmt.Errorf("flag --version is required")
|
||||
}
|
||||
version, _ := cmd.Flags().GetInt("version")
|
||||
exists, err := docVersionExists(cmd.Context(), nodeID, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("文档版本 %d 不存在,已停止回滚;请先执行 dws doc version list --node %s --format json 获取可回滚版本", version, nodeID)
|
||||
}
|
||||
if !confirmDangerousAction(cmd, fmt.Sprintf("revert document to version %d", version), nodeID) {
|
||||
return nil
|
||||
if !commandDryRun(cmd) {
|
||||
exists, err := docVersionExists(cmd.Context(), nodeID, version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("文档版本 %d 不存在,已停止回滚;请先执行 dws doc version list --node %s --format json 获取可回滚版本", version, nodeID)
|
||||
}
|
||||
if !confirmDangerousAction(cmd, fmt.Sprintf("revert document to version %d", version), nodeID) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return callMCPToolOnServer("doc", "revert_doc_version", map[string]any{
|
||||
"nodeId": nodeID,
|
||||
|
||||
@@ -834,7 +834,8 @@ func newDriveCommand() *cobra.Command {
|
||||
driveRenameCmd := &cobra.Command{
|
||||
Use: "rename",
|
||||
Short: "重命名文件/文档",
|
||||
Long: `修改文档空间中文档或文件的名称。
|
||||
Long: `修改文档空间中文档或文件的名称。服务端会保留节点的原扩展名;
|
||||
如果 --name 带常见文件扩展名,CLI 会自动去掉一层,避免出现 report.txt.txt。
|
||||
|
||||
权限要求: 对文档有"编辑"权限。`,
|
||||
Example: ` dws drive rename --node DOC_ID --name "新名称"`,
|
||||
@@ -848,12 +849,12 @@ func newDriveCommand() *cobra.Command {
|
||||
}
|
||||
return callMCPToolOnServer("doc", "rename_document", map[string]any{
|
||||
"nodeId": nodeID,
|
||||
"newName": flagOrFallback(cmd, "name", "title"),
|
||||
"newName": renameBaseName(flagOrFallback(cmd, "name", "title")),
|
||||
})
|
||||
},
|
||||
}
|
||||
driveRenameCmd.Flags().String("node", "", "文档/文件 ID 或 URL (必填)")
|
||||
driveRenameCmd.Flags().String("name", "", "新名称 (必填)")
|
||||
driveRenameCmd.Flags().String("name", "", "新名称 (必填;服务端保留原扩展名,传入常见扩展名时 CLI 会自动去掉一层)")
|
||||
|
||||
driveStatsCmd := &cobra.Command{
|
||||
Use: "stats",
|
||||
|
||||
@@ -129,7 +129,7 @@ func newOaCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
instanceId := mustGetFlag(cmd, "instance-id")
|
||||
if !confirmDelete("审批实例", instanceId) {
|
||||
if !commandDryRun(cmd) && !confirmDelete("审批实例", instanceId) {
|
||||
return nil
|
||||
}
|
||||
argsMap := map[string]any{
|
||||
|
||||
@@ -282,6 +282,10 @@ func newTodoCommand() *cobra.Command {
|
||||
todoTaskGetCmd := &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "待办详情",
|
||||
Long: `查询单条待办的详情。
|
||||
|
||||
当前上游详情接口不返回 reminderRules;本命令不能读取或验证 add-reminder /
|
||||
reset-reminder 写入的提醒规则。提醒写命令的成功响应只能作为写入回执。`,
|
||||
Example: ` dws todo task get --task-id <taskId>
|
||||
|
||||
# 查询 taskId: dws todo task list`,
|
||||
@@ -427,6 +431,10 @@ func newTodoCommand() *cobra.Command {
|
||||
todoTaskAddReminderCmd := &cobra.Command{
|
||||
Use: "add-reminder",
|
||||
Short: "添加待办提醒",
|
||||
Long: `为已有待办写入一条提醒规则。
|
||||
|
||||
当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;
|
||||
成功响应是写入回执,不代表 CLI 能再次读取并核验该规则。`,
|
||||
Example: ` dws todo task add-reminder --task-id <taskId> --base-time dueTime --due-date-offset -30
|
||||
dws todo task add-reminder --task-id <taskId> --base-time customTime --reminder-time-stamp 2026-03-10T18:00:00+08:00
|
||||
# 查询 taskId: dws todo task list
|
||||
@@ -473,6 +481,10 @@ func newTodoCommand() *cobra.Command {
|
||||
todoTaskUpdateReminderCmd := &cobra.Command{
|
||||
Use: "reset-reminder",
|
||||
Short: "重置待办提醒",
|
||||
Long: `整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。
|
||||
|
||||
当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;
|
||||
成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。`,
|
||||
Example: ` dws todo task reset-reminder --task-id <taskId>
|
||||
dws todo task reset-reminder --task-id <taskId> --reminder-rules <reminderRules>
|
||||
# 查询 taskId: dws todo task list
|
||||
@@ -924,7 +936,7 @@ func rejectUnsupportedTodoReminderFlags(cmd *cobra.Command) error {
|
||||
return &CLIError{
|
||||
Code: CodeMissingParam,
|
||||
Message: fmt.Sprintf("todo 当前不支持独立 reminder 参数: %s", strings.Join(unsupported, ", ")),
|
||||
Suggestion: "请使用 --due 表示截止时间;如果用户要的是精确提醒时间,请明确说明该能力当前不支持,而不是把 --due 当作 reminder。",
|
||||
Suggestion: "请先用 --due 设置截止时间;需要独立提醒时,再用 dws todo task add-reminder。当前上游不支持读取提醒规则,不能用 task get/list 验证写入结果。",
|
||||
Operation: "todo.task.reminder",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
||||
)
|
||||
|
||||
// Remind: create a personal todo for YOURSELF with an optional due/reminder time,
|
||||
// Remind: create a personal todo for YOURSELF with an optional due time,
|
||||
// in one command. It resolves the current user and explicitly passes executorIds;
|
||||
// the real todo backend does not reliably default a missing executor to "me".
|
||||
//
|
||||
@@ -35,13 +35,13 @@ var Remind = shortcut.Shortcut{
|
||||
Service: "todo",
|
||||
Command: "+remind",
|
||||
Product: "todo",
|
||||
Description: "给自己创建一条带截止/提醒时间的待办",
|
||||
Intent: "当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;" +
|
||||
"内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。",
|
||||
Description: "给自己创建一条带可选截止时间的待办",
|
||||
Intent: "当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;" +
|
||||
"内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。",
|
||||
Risk: shortcut.RiskWrite,
|
||||
Flags: []shortcut.Flag{
|
||||
{Name: "task", Type: shortcut.FlagString, Desc: "待办标题/内容", Required: true},
|
||||
{Name: "at", Type: shortcut.FlagString, Desc: "截止/提醒时间(ISO8601,可选,如 2026-03-10T18:00:00+08:00)"},
|
||||
{Name: "at", Type: shortcut.FlagString, Desc: "截止时间(ISO8601,可选,不是提醒时间;如 2026-03-10T18:00:00+08:00)"},
|
||||
},
|
||||
Tips: []string{`dws todo +remind --task "交周报" --at 2026-03-10T18:00:00+08:00`},
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
@@ -59,7 +59,7 @@ var Remind = shortcut.Shortcut{
|
||||
"executorIds": []string{userID},
|
||||
}
|
||||
|
||||
// Optional due/reminder time. The todo helper feeds --due through
|
||||
// Optional due time. The todo helper feeds --due through
|
||||
// parseISOTimeToMillis and stores dueTime as epoch milliseconds (int64),
|
||||
// so we do the same here rather than passing a raw string.
|
||||
if rt.Changed("at") {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
package smart
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRemindShortcutDoesNotAdvertiseDueTimeAsReminder(t *testing.T) {
|
||||
if strings.Contains(Remind.Description, "提醒时间") || strings.Contains(Remind.Intent, "截止/提醒") {
|
||||
t.Fatalf("shortcut contract still conflates dueTime with a reminder: %#v", Remind)
|
||||
}
|
||||
for _, flag := range Remind.Flags {
|
||||
if flag.Name == "at" && !strings.Contains(flag.Desc, "不是提醒时间") {
|
||||
t.Fatalf("--at description = %q, want explicit dueTime boundary", flag.Desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
| "搜一下有没有叫XX的文件" | 全局搜索 | `drive search` | `wiki node search` | 未指定空间 → drive search 全局聚合搜索 |
|
||||
| "在知识库里创建一个文档" | 创建空文件实体 | `wiki node create --type adoc` | `doc create` | 空间内创建节点归 wiki;doc create 是向已有文档写入内容,不是创建文件节点 |
|
||||
| "帮我建一个明天下午的日程" | 日历日程 | `calendar` | — | 日历日程管理(可含参与者/会议室);视频会议(conference)当前开源 CLI 不支持 |
|
||||
| "明早 9 点提醒我提交周报" | 创建个人待办,但需先声明 reminder 边界 | `todo` | `calendar` | todo 当前只支持 dueTime 截止时间,不支持独立精确 reminder |
|
||||
| "明早 9 点提醒我提交周报" | 创建个人待办后添加提醒 | `todo task create` + `todo task add-reminder` | `calendar` | `+remind --at` 只写截止时间;提醒可写入,但上游不支持规则读回 |
|
||||
| "帮我建一个项目群" | 创建群聊 | `chat group create` | — | 群聊管理,不是日历日程 |
|
||||
| "把张三拉进群" | 添加群成员 | `chat group members add` | — | 先查 userId,再添加 |
|
||||
| "通知群里的人都来开会" | 个人身份群发 | `chat message send` | `chat message send-by-bot` | 以个人身份向群发消息 |
|
||||
@@ -330,7 +330,7 @@ dws chat message send --open-dingtalk-id <openDingTalkId> --msg-type file --file
|
||||
|
||||
**用 `todo` 的场景**:
|
||||
- "记一下这周要做的事" — 个人任务管理
|
||||
- "创建一个待办提醒" — 仍归 `todo`,但要先说明当前只有 dueTime 截止时间,没有独立 reminder schedule
|
||||
- "创建一个待办提醒" — 仍归 `todo`:先创建待办,再用 `task add-reminder` 写入;需说明上游不支持读取 reminderRules,不能写后读回核验
|
||||
|
||||
**判断关键**:钉钉日志系统(日报/周报模版,含按模版创建汇报)→ `report`;文档/知识库长文→ `doc`;任务清单→ `todo`
|
||||
|
||||
|
||||
@@ -457,9 +457,10 @@ dws todo task list-sub --task-id <taskId> --format json
|
||||
|
||||
- 优先级值: 10=低, 20=普通, 30=较高, 40=紧急
|
||||
- `--due` 是截止时间 dueTime,不是提醒时间;使用 ISO-8601 格式(如 2026-03-10T18:00:00+08:00)
|
||||
- 当前不支持单独的 `reminder` / `remind-at` 精确提醒能力;不要把 `--due` 解释成“几点提醒”
|
||||
- `todo +remind --at` 同样只写截止时间 dueTime,不会创建独立提醒规则;不要把它解释成“几点提醒”
|
||||
- `--recurrence`:仅在与 `--due` 同时设置时有效;当前仅支持按天循环。字符串内需含换行,示例:`DTSTART:20260320T020000Z\nRRULE:FREQ=DAILY;INTERVAL=1`(DTSTART 表示首次截止时间,需与业务约定一致)
|
||||
- 若用户的真实诉求是“到点提醒我”,需要先说明能力边界;当前 CLI 只能表达 deadline / recurrence,不能表达独立 reminder schedule
|
||||
- 独立提醒可在待办创建后通过 `task add-reminder` 写入,或用 `task reset-reminder` 整体替换/清除
|
||||
- 当前上游没有提醒规则查询接口,`task get/list` 均不返回 `reminderRules`;写命令成功响应只能作为写入回执,不能声称已读回核验
|
||||
- `task list` 的 `--status` 对应 MCP `get_user_todos_in_current_org` 的 `todoStatus` 参数
|
||||
- `task list --query-all` 查询当前用户跨组织的全部待办;不传时保持当前组织范围
|
||||
- todo 是个人待办管理产品
|
||||
|
||||
@@ -39,7 +39,7 @@ metadata:
|
||||
| `dws todo +list-comment` | read | 查询待办评论列表 |
|
||||
| `dws todo +list-sub` | read | 查询子待办列表 |
|
||||
| `dws todo +overdue` | read | 列出我已过期未完成的待办 |
|
||||
| `dws todo +remind` | write | 给自己创建一条带截止/提醒时间的待办 |
|
||||
| `dws todo +remind` | write | 给自己创建一条带可选截止时间的待办(`--at` 只写 dueTime,不创建提醒规则) |
|
||||
| `dws todo +todo-done` | write | 按标题关键词把我的某条待办标记完成(自动定位 taskId) |
|
||||
<!-- VISIBLE_SHORTCUTS_END -->
|
||||
|
||||
|
||||
@@ -455,9 +455,10 @@ dws todo task list-sub --task-id <taskId> --format json
|
||||
|
||||
- 优先级值: 10=低, 20=普通, 30=较高, 40=紧急
|
||||
- `--due` 是截止时间 dueTime,不是提醒时间;使用 ISO-8601 格式(如 2026-03-10T18:00:00+08:00)
|
||||
- 当前不支持单独的 `reminder` / `remind-at` 精确提醒能力;不要把 `--due` 解释成“几点提醒”
|
||||
- `todo +remind --at` 同样只写截止时间 dueTime,不会创建独立提醒规则;不要把它解释成“几点提醒”
|
||||
- `--recurrence`:仅在与 `--due` 同时设置时有效;当前仅支持按天循环。字符串内需含换行,示例:`DTSTART:20260320T020000Z\nRRULE:FREQ=DAILY;INTERVAL=1`(DTSTART 表示首次截止时间,需与业务约定一致)
|
||||
- 若用户的真实诉求是“到点提醒我”,需要先说明能力边界;当前 CLI 只能表达 deadline / recurrence,不能表达独立 reminder schedule
|
||||
- 独立提醒可在待办创建后通过 `task add-reminder` 写入,或用 `task reset-reminder` 整体替换/清除
|
||||
- 当前上游没有提醒规则查询接口,`task get/list` 均不返回 `reminderRules`;写命令成功响应只能作为写入回执,不能声称已读回核验
|
||||
- `task list` 的 `--status` 对应 MCP `get_user_todos_in_current_org` 的 `todoStatus` 参数
|
||||
- `task list --query-all` 查询当前用户跨组织的全部待办;不传时保持当前组织范围
|
||||
- todo 是个人待办管理产品
|
||||
|
||||
Reference in New Issue
Block a user