fix: address contract review feedback
This commit is contained in:
@@ -12071,7 +12071,8 @@
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -12121,7 +12122,8 @@
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -12131,7 +12133,8 @@
|
||||
{
|
||||
"value": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -12277,7 +12280,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -12286,7 +12289,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -12318,7 +12321,7 @@
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#doc.rename_document"
|
||||
],
|
||||
"use_when": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
]
|
||||
},
|
||||
"doc search": {
|
||||
|
||||
@@ -7083,12 +7083,13 @@
|
||||
]
|
||||
},
|
||||
"drive rename": {
|
||||
"agent_summary": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"agent_summary": "安全重命名文档空间中的文档、文件或文件夹,并按节点真实扩展名避免双后缀",
|
||||
"agent_summary_source": "dws-agent-selection/drive",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -7098,14 +7099,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 +7119,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist."
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -7138,7 +7139,8 @@
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -7148,7 +7150,8 @@
|
||||
{
|
||||
"value": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -7224,14 +7227,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist."
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -7246,14 +7249,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "doc.rename_document",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist."
|
||||
},
|
||||
{
|
||||
"value": "doc.rename_document",
|
||||
@@ -7268,14 +7271,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension."
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
@@ -7302,7 +7305,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -7311,7 +7314,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -7342,7 +7345,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:696955f889cbb3a843a1afc91ee511cd54cf1dd5d96b0dabc20516f59719eecd",
|
||||
"source_hash": "sha256:f71480e58eda8f2be041e07d7faf6fb20f5c21f861995d3c4dacc311b46c3e81",
|
||||
"surface_hash": "sha256:4cf8460240b19f896c3a330c69982cbb5f57aa8576cdf30373172082eea893ed",
|
||||
"coverage": {
|
||||
"surface_products": 26,
|
||||
|
||||
@@ -2324,11 +2324,12 @@
|
||||
]
|
||||
},
|
||||
"todo +remind": {
|
||||
"agent_summary": "给自己创建一条带截止/提醒时间的待办",
|
||||
"agent_summary": "给自己创建一条带可选截止时间的待办",
|
||||
"agent_summary_source": "dws-agent-selection/todo",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
],
|
||||
"confirmation": "user_required",
|
||||
"effect": "write",
|
||||
@@ -2338,14 +2339,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "给自己创建一条带截止/提醒时间的待办",
|
||||
"value": "给自己创建一条带可选截止时间的待办",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "给自己创建一条带截止/提醒时间的待办",
|
||||
"value": "给自己创建一条带可选截止时间的待办",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -2371,7 +2372,8 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -2380,7 +2382,8 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -2546,7 +2549,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -2555,7 +2558,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -2578,7 +2581,7 @@
|
||||
"internal/cli/schema_hints/selection/todo.json"
|
||||
],
|
||||
"use_when": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
]
|
||||
},
|
||||
"todo +todo-done": {
|
||||
@@ -8604,14 +8607,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "available",
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed"
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms."
|
||||
},
|
||||
{
|
||||
"value": "available",
|
||||
@@ -8714,14 +8717,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "mcp",
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed"
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms."
|
||||
},
|
||||
{
|
||||
"value": "mcp",
|
||||
@@ -8750,14 +8753,14 @@
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"candidates": [
|
||||
{
|
||||
"value": true,
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed"
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms."
|
||||
},
|
||||
{
|
||||
"value": true,
|
||||
@@ -8784,7 +8787,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -8793,7 +8796,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -8823,7 +8826,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:696955f889cbb3a843a1afc91ee511cd54cf1dd5d96b0dabc20516f59719eecd",
|
||||
"source_hash": "sha256:f71480e58eda8f2be041e07d7faf6fb20f5c21f861995d3c4dacc311b46c3e81",
|
||||
"surface_hash": "sha256:4cf8460240b19f896c3a330c69982cbb5f57aa8576cdf30373172082eea893ed",
|
||||
"source_files": 160,
|
||||
"hint_files": 54,
|
||||
@@ -3890,7 +3890,7 @@
|
||||
{
|
||||
"tool_path": "todo task remove-attachment",
|
||||
"source": "skills/mono/references/products/todo.md",
|
||||
"line": 438,
|
||||
"line": 440,
|
||||
"candidates": [
|
||||
"todo task add-attachment",
|
||||
"todo task add-executor",
|
||||
@@ -3904,7 +3904,7 @@
|
||||
{
|
||||
"tool_path": "todo task list-sub",
|
||||
"source": "skills/mono/references/products/todo.md",
|
||||
"line": 441,
|
||||
"line": 443,
|
||||
"candidates": [
|
||||
"todo task add-attachment",
|
||||
"todo task add-executor",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:a0e007ce9500f18abfca09ad2de259b8b574b8150c9c79ccc7f3d7b1ffe66f7d",
|
||||
"source_hash": "sha256:91a6b7040ccc8fa16dce388a1ea8ac2aae808819af2e6e998f65f363685ca325",
|
||||
"surface_hash": "sha256:4cf8460240b19f896c3a330c69982cbb5f57aa8576cdf30373172082eea893ed",
|
||||
"catalog": {
|
||||
"agent_metadata": {
|
||||
"products_with_metadata": 26,
|
||||
"source": "embedded-skill-metadata",
|
||||
"source_hash": "sha256:696955f889cbb3a843a1afc91ee511cd54cf1dd5d96b0dabc20516f59719eecd",
|
||||
"source_hash": "sha256:f71480e58eda8f2be041e07d7faf6fb20f5c21f861995d3c4dacc311b46c3e81",
|
||||
"surface_hash": "sha256:4cf8460240b19f896c3a330c69982cbb5f57aa8576cdf30373172082eea893ed",
|
||||
"surface_products": 26,
|
||||
"surface_tools": 813,
|
||||
@@ -15008,7 +15008,8 @@
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
],
|
||||
"canonical_path": "doc.rename_document",
|
||||
"cli_name": "rename",
|
||||
@@ -15029,7 +15030,7 @@
|
||||
"risk": "medium",
|
||||
"title": "重命名文档/文件",
|
||||
"use_when": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -16613,18 +16614,19 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"agent_summary": "安全重命名文档空间中的文档、文件或文件夹,并按节点真实扩展名避免双后缀",
|
||||
"agent_summary_source": "dws-agent-selection/drive",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
],
|
||||
"canonical_path": "drive.rename_document",
|
||||
"cli_name": "rename",
|
||||
"cli_path": "drive rename",
|
||||
"confirmation": "not_required",
|
||||
"description": "修改文档空间中文档或文件的名称。服务端会保留节点的原扩展名;\n如果 --name 带常见文件扩展名,CLI 会自动去掉一层,避免出现 report.txt.txt。\n\n权限要求: 对文档有\"编辑\"权限。",
|
||||
"description": "修改文档空间中文档、文件或文件夹的名称。\n实际执行前会读取节点类型和当前扩展名:文件的新名称仅在末尾扩展名与当前扩展名一致时去掉一层,\n避免 report.txt.txt;文件夹和扩展名不匹配的名称保持不变。dry-run 不读取远端元数据,因此保留输入名称。\n\n权限要求: 对文档有\"编辑\"权限。",
|
||||
"effect": "write",
|
||||
"idempotency": "unknown",
|
||||
"interface_mode": "mcp",
|
||||
@@ -16639,7 +16641,7 @@
|
||||
"risk": "medium",
|
||||
"title": "重命名文件/文档",
|
||||
"use_when": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -24777,7 +24779,7 @@
|
||||
"cli_name": "reset-reminder",
|
||||
"cli_path": "todo task reset-reminder",
|
||||
"confirmation": "not_required",
|
||||
"description": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。",
|
||||
"description": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。显式传值必须是合法 JSON 数组,\n每条规则必须按 baseTime 提供 dueDateOffset 或 reminderTimeStamp;非法输入会在远端调用前失败。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。",
|
||||
"effect": "write",
|
||||
"group": "task",
|
||||
"idempotency": "unknown",
|
||||
@@ -24793,7 +24795,7 @@
|
||||
"risk": "medium",
|
||||
"title": "重置待办提醒",
|
||||
"use_when": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -25032,11 +25034,12 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "给自己创建一条带截止/提醒时间的待办",
|
||||
"agent_summary": "给自己创建一条带可选截止时间的待办",
|
||||
"agent_summary_source": "dws-agent-selection/todo",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
],
|
||||
"canonical_path": "todo.shortcut_remind",
|
||||
"cli_name": "+remind",
|
||||
@@ -25053,7 +25056,7 @@
|
||||
"risk": "medium",
|
||||
"title": "给自己创建一条带可选截止时间的待办",
|
||||
"use_when": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -352880,7 +352883,8 @@
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
],
|
||||
"canonical_path": "doc.rename_document",
|
||||
"cli_name": "rename",
|
||||
@@ -352941,7 +352945,8 @@
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"value": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -352951,7 +352956,8 @@
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"value": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -353173,7 +353179,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"value": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -353182,7 +353188,7 @@
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/doc.json",
|
||||
"value": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -353425,7 +353431,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "重命名文档/文件",
|
||||
"use_when": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
]
|
||||
},
|
||||
"doc.reply_comment": {
|
||||
@@ -390427,18 +390433,19 @@
|
||||
"structured-hint:internal/cli/schema_hints/products/drive.json",
|
||||
"structured-hint:internal/cli/schema_hints/selection-review.json#drive.rename_document"
|
||||
],
|
||||
"agent_summary": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"agent_summary": "安全重命名文档空间中的文档、文件或文件夹,并按节点真实扩展名避免双后缀",
|
||||
"agent_summary_source": "dws-agent-selection/drive",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
],
|
||||
"canonical_path": "drive.rename_document",
|
||||
"cli_name": "rename",
|
||||
"cli_path": "drive rename",
|
||||
"confirmation": "not_required",
|
||||
"description": "修改文档空间中文档或文件的名称。服务端会保留节点的原扩展名;\n如果 --name 带常见文件扩展名,CLI 会自动去掉一层,避免出现 report.txt.txt。\n\n权限要求: 对文档有\"编辑\"权限。",
|
||||
"description": "修改文档空间中文档、文件或文件夹的名称。\n实际执行前会读取节点类型和当前扩展名:文件的新名称仅在末尾扩展名与当前扩展名一致时去掉一层,\n避免 report.txt.txt;文件夹和扩展名不匹配的名称保持不变。dry-run 不读取远端元数据,因此保留输入名称。\n\n权限要求: 对文档有\"编辑\"权限。",
|
||||
"display": "钉盘文件管理",
|
||||
"effect": "write",
|
||||
"effect_source": "command-verb",
|
||||
@@ -390453,20 +390460,20 @@
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名"
|
||||
"value": "安全重命名文档空间中的文档、文件或文件夹,并按节点真实扩展名避免双后缀"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名"
|
||||
"value": "安全重命名文档空间中的文档、文件或文件夹,并按节点真实扩展名避免双后缀"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": "available"
|
||||
@@ -390480,7 +390487,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": "available"
|
||||
},
|
||||
@@ -390493,7 +390500,8 @@
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -390503,7 +390511,8 @@
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -390517,7 +390526,7 @@
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_manual",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"selected": false,
|
||||
"source": "reviewed_manual_hint",
|
||||
"source_ref": "drive rename",
|
||||
@@ -390550,7 +390559,7 @@
|
||||
"precedence": "cobra_help",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"value": "修改文档空间中文档或文件的名称。服务端会保留节点的原扩展名;\n如果 --name 带常见文件扩展名,CLI 会自动去掉一层,避免出现 report.txt.txt。\n\n权限要求: 对文档有\"编辑\"权限。"
|
||||
"value": "修改文档空间中文档、文件或文件夹的名称。\n实际执行前会读取节点类型和当前扩展名:文件的新名称仅在末尾扩展名与当前扩展名一致时去掉一层,\n避免 report.txt.txt;文件夹和扩展名不匹配的名称保持不变。dry-run 不读取远端元数据,因此保留输入名称。\n\n权限要求: 对文档有\"编辑\"权限。"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
@@ -390562,7 +390571,7 @@
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"value": "修改文档空间中文档或文件的名称。服务端会保留节点的原扩展名;\n如果 --name 带常见文件扩展名,CLI 会自动去掉一层,避免出现 report.txt.txt。\n\n权限要求: 对文档有\"编辑\"权限。"
|
||||
"value": "修改文档空间中文档、文件或文件夹的名称。\n实际执行前会读取节点类型和当前扩展名:文件的新名称仅在末尾扩展名与当前扩展名一致时去掉一层,\n避免 report.txt.txt;文件夹和扩展名不匹配的名称保持不变。dry-run 不读取远端元数据,因此保留输入名称。\n\n权限要求: 对文档有\"编辑\"权限。"
|
||||
},
|
||||
"effect": {
|
||||
"candidates": [
|
||||
@@ -390616,7 +390625,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": "mcp"
|
||||
@@ -390630,7 +390639,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": "mcp"
|
||||
},
|
||||
@@ -390638,7 +390647,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": {
|
||||
@@ -390655,7 +390664,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": {
|
||||
"product_id": "doc",
|
||||
@@ -390680,7 +390689,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": true
|
||||
@@ -390695,7 +390704,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"source": "internal/cli/schema_hints/metadata/drive.json",
|
||||
"value": true
|
||||
},
|
||||
@@ -390741,7 +390750,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -390750,7 +390759,7 @@
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -390767,22 +390776,22 @@
|
||||
"parameter_count": 2,
|
||||
"parameters": {
|
||||
"name": {
|
||||
"description": "新显示名称;服务端保留节点原扩展名,若传入常见文件扩展名,CLI 会自动去掉一层以避免双扩展名",
|
||||
"description": "新显示名称;实际执行前读取节点类型与当前扩展名,仅对非文件夹且末尾后缀与当前扩展名一致的名称去掉一层,避免双扩展名",
|
||||
"field_provenance": {
|
||||
"description": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_manual",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"selected": true,
|
||||
"source": "reviewed_manual_hint",
|
||||
"value": "新显示名称;服务端保留节点原扩展名,若传入常见文件扩展名,CLI 会自动去掉一层以避免双扩展名"
|
||||
"value": "新显示名称;实际执行前读取节点类型与当前扩展名,仅对非文件夹且末尾后缀与当前扩展名一致的名称去掉一层,避免双扩展名"
|
||||
},
|
||||
{
|
||||
"precedence": "cobra_contract",
|
||||
"selected": false,
|
||||
"source": "cobra_usage",
|
||||
"value": "新名称 (必填;服务端保留原扩展名,传入常见扩展名时 CLI 会自动去掉一层)"
|
||||
"value": "新名称 (必填;实际执行时仅去掉与节点当前扩展名完全匹配的一层后缀)"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
@@ -390799,9 +390808,9 @@
|
||||
],
|
||||
"precedence": "reviewed_manual",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"source": "reviewed_manual_hint",
|
||||
"value": "新显示名称;服务端保留节点原扩展名,若传入常见文件扩展名,CLI 会自动去掉一层以避免双扩展名"
|
||||
"value": "新显示名称;实际执行前读取节点类型与当前扩展名,仅对非文件夹且末尾后缀与当前扩展名一致的名称去掉一层,避免双扩展名"
|
||||
},
|
||||
"property": {
|
||||
"candidates": [
|
||||
@@ -391001,7 +391010,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "重命名文件/文档",
|
||||
"use_when": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
|
||||
]
|
||||
},
|
||||
"drive.search_files": {
|
||||
@@ -566362,7 +566371,7 @@
|
||||
"cli_name": "reset-reminder",
|
||||
"cli_path": "todo task reset-reminder",
|
||||
"confirmation": "not_required",
|
||||
"description": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。",
|
||||
"description": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。显式传值必须是合法 JSON 数组,\n每条规则必须按 baseTime 提供 dueDateOffset 或 reminderTimeStamp;非法输入会在远端调用前失败。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。",
|
||||
"display": "待办任务管理",
|
||||
"effect": "write",
|
||||
"effect_source": "command-verb",
|
||||
@@ -566391,7 +566400,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"value": "available"
|
||||
@@ -566405,7 +566414,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"value": "available"
|
||||
},
|
||||
@@ -566441,6 +566450,14 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"source_ref": "todo task reset-reminder",
|
||||
"value": "todo.reset_todo_reminder"
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_manual",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"selected": false,
|
||||
"source": "reviewed_manual_hint",
|
||||
"source_ref": "todo task reset-reminder",
|
||||
"value": "todo.reset_todo_reminder"
|
||||
}
|
||||
],
|
||||
"precedence": "command_registry",
|
||||
@@ -566469,7 +566486,7 @@
|
||||
"precedence": "cobra_help",
|
||||
"selected": true,
|
||||
"source": "cobra_help",
|
||||
"value": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。"
|
||||
"value": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。显式传值必须是合法 JSON 数组,\n每条规则必须按 baseTime 提供 dueDateOffset 或 reminderTimeStamp;非法输入会在远端调用前失败。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。"
|
||||
},
|
||||
{
|
||||
"precedence": "mcp_metadata",
|
||||
@@ -566481,7 +566498,7 @@
|
||||
"precedence": "cobra_help",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_help",
|
||||
"value": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。"
|
||||
"value": "整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。显式传值必须是合法 JSON 数组,\n每条规则必须按 baseTime 提供 dueDateOffset 或 reminderTimeStamp;非法输入会在远端调用前失败。\n\n当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;\n成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。"
|
||||
},
|
||||
"effect": {
|
||||
"candidates": [
|
||||
@@ -566537,7 +566554,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"value": "mcp"
|
||||
@@ -566551,7 +566568,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"value": "mcp"
|
||||
},
|
||||
@@ -566593,7 +566610,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"value": true
|
||||
@@ -566608,7 +566625,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "dws-tool-metadata/todo marks this tool as reviewed",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"source": "internal/cli/schema_hints/metadata/todo.json",
|
||||
"value": true
|
||||
},
|
||||
@@ -566654,7 +566671,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -566663,7 +566680,7 @@
|
||||
"review_reason": "结合本机 dws schema 实时描述、Skill 与 Cobra 手写选型;pinned schema_mcp_metadata 仅对照。",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -566681,15 +566698,22 @@
|
||||
"parameter_count": 2,
|
||||
"parameters": {
|
||||
"reminder-rules": {
|
||||
"description": "提醒规则 JSON 数组 (可选,为空则清除提醒)",
|
||||
"description": "提醒规则 JSON 数组;不传表示清除,显式传值必须为对象数组,且每条按 baseTime 提供整数 dueDateOffset 或 ISO8601 reminderTimeStamp",
|
||||
"field_provenance": {
|
||||
"description": {
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "cobra_contract",
|
||||
"precedence": "reviewed_manual",
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"selected": true,
|
||||
"source": "reviewed_manual_hint",
|
||||
"value": "提醒规则 JSON 数组;不传表示清除,显式传值必须为对象数组,且每条按 baseTime 提供整数 dueDateOffset 或 ISO8601 reminderTimeStamp"
|
||||
},
|
||||
{
|
||||
"precedence": "cobra_contract",
|
||||
"selected": false,
|
||||
"source": "cobra_usage",
|
||||
"value": "提醒规则 JSON 数组 (可选,为空则清除提醒)"
|
||||
"value": "提醒规则 JSON 数组 (不传则清除;显式传值必须合法)"
|
||||
},
|
||||
{
|
||||
"precedence": "default",
|
||||
@@ -566698,10 +566722,11 @@
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"precedence": "cobra_contract",
|
||||
"precedence": "reviewed_manual",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "cobra_usage",
|
||||
"value": "提醒规则 JSON 数组 (可选,为空则清除提醒)"
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"source": "reviewed_manual_hint",
|
||||
"value": "提醒规则 JSON 数组;不传表示清除,显式传值必须为对象数组,且每条按 baseTime 提供整数 dueDateOffset 或 ISO8601 reminderTimeStamp"
|
||||
},
|
||||
"property": {
|
||||
"candidates": [
|
||||
@@ -566875,7 +566900,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "重置待办提醒",
|
||||
"use_when": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
|
||||
]
|
||||
},
|
||||
"todo.shortcut_assign": {
|
||||
@@ -571429,11 +571454,12 @@
|
||||
"internal/cli/schema_hints/metadata/todo.json",
|
||||
"internal/cli/schema_hints/selection/todo.json"
|
||||
],
|
||||
"agent_summary": "给自己创建一条带截止/提醒时间的待办",
|
||||
"agent_summary": "给自己创建一条带可选截止时间的待办",
|
||||
"agent_summary_source": "dws-agent-selection/todo",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
],
|
||||
"canonical_path": "todo.shortcut_remind",
|
||||
"cli_name": "+remind",
|
||||
@@ -571454,14 +571480,14 @@
|
||||
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": "给自己创建一条带截止/提醒时间的待办"
|
||||
"value": "给自己创建一条带可选截止时间的待办"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": "给自己创建一条带截止/提醒时间的待办"
|
||||
"value": "给自己创建一条带可选截止时间的待办"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -571487,7 +571513,8 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -571496,7 +571523,8 @@
|
||||
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -571706,7 +571734,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -571715,7 +571743,7 @@
|
||||
"review_reason": "Agent-authored and reviewed from the built-in Shortcut intent, executable Cobra path, and declared outcome; it affects selection only and does not alter execution or safety facts.",
|
||||
"source": "internal/cli/schema_hints/selection/todo.json",
|
||||
"value": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -571916,7 +571944,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "给自己创建一条带可选截止时间的待办",
|
||||
"use_when": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
]
|
||||
},
|
||||
"todo.shortcut_todo_done": {
|
||||
|
||||
@@ -206,10 +206,10 @@
|
||||
"reviewed": true,
|
||||
"parameters": {
|
||||
"name": {
|
||||
"description": "新显示名称;服务端保留节点原扩展名,若传入常见文件扩展名,CLI 会自动去掉一层以避免双扩展名"
|
||||
"description": "新显示名称;实际执行前读取节点类型与当前扩展名,仅对非文件夹且末尾后缀与当前扩展名一致的名称去掉一层,避免双扩展名"
|
||||
}
|
||||
},
|
||||
"review_reason": "The reviewed CLI normalizes one familiar extension from --name because rename_document preserves the node's existing extension.",
|
||||
"review_reason": "The reviewed CLI reads node metadata before mutation and strips only the exact current extension for non-folder nodes, avoiding both duplicate extensions and dotted-folder corruption without a suffix whitelist.",
|
||||
"cli_path": "drive rename"
|
||||
},
|
||||
"drive.search_files": {
|
||||
|
||||
@@ -126,7 +126,14 @@
|
||||
"todo.reset_todo_reminder": {
|
||||
"interface_mode": "mcp",
|
||||
"availability": "available",
|
||||
"reviewed": true
|
||||
"reviewed": true,
|
||||
"parameters": {
|
||||
"reminder-rules": {
|
||||
"description": "提醒规则 JSON 数组;不传表示清除,显式传值必须为对象数组,且每条按 baseTime 提供整数 dueDateOffset 或 ISO8601 reminderTimeStamp"
|
||||
}
|
||||
},
|
||||
"review_reason": "The reviewed CLI rejects malformed or structurally invalid non-empty reminder rules before the reset mutation, while preserving omission and [] as explicit clear forms.",
|
||||
"cli_path": "todo task reset-reminder"
|
||||
},
|
||||
"todo.update_todo_done_status": {
|
||||
"interface_mode": "mcp",
|
||||
|
||||
@@ -582,11 +582,12 @@
|
||||
"doc.rename_document": {
|
||||
"agent_summary": "兼容入口:重命名文档或文件;文件重命名能力已迁移到 drive。",
|
||||
"use_when": [
|
||||
"用户要改文档/文件在列表与链接中展示的名称时;即使名称带常见扩展名,CLI 也会去掉一层再交给保留原扩展名的服务端"
|
||||
"用户要改在线文档在列表与链接中展示的名称时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"改正文标题/章节标题改用 doc block update",
|
||||
"不要用 update 或重新 create 来改名"
|
||||
"不要用 update 或重新 create 来改名",
|
||||
"文件或文件夹重命名优先用 dws drive rename,由该命令读取真实节点类型和当前扩展名"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc rename --node <DOC_ID> --name \"新名称\" --format json"
|
||||
|
||||
@@ -545,13 +545,14 @@
|
||||
]
|
||||
},
|
||||
"drive.rename_document": {
|
||||
"agent_summary": "修改文档空间中文档或文件的名称,并避免服务端保留原扩展名导致双扩展名",
|
||||
"agent_summary": "安全重命名文档空间中的文档、文件或文件夹,并按节点真实扩展名避免双后缀",
|
||||
"use_when": [
|
||||
"用户要重命名钉盘/文档空间中的文件或文档显示名称时;--name 带常见扩展名也可,CLI 会自动去掉一层"
|
||||
"用户要重命名钉盘/文档空间中的文件、文档或文件夹时;实际执行会读取节点类型和当前扩展名,仅去掉完全匹配的一层后缀"
|
||||
],
|
||||
"avoid_when": [
|
||||
"要改正文里的标题/章节 H1 改用 dws doc block update,不要用 rename",
|
||||
"只要复制或移动改用 copy/move"
|
||||
"只要复制或移动改用 copy/move",
|
||||
"dry-run 不读取节点元数据,输出名称尚未做基于当前扩展名的规范化"
|
||||
],
|
||||
"examples": [
|
||||
"dws drive rename --node <ID> --name \"新名称\" --format json"
|
||||
|
||||
@@ -360,7 +360,7 @@
|
||||
"todo.reset_todo_reminder": {
|
||||
"agent_summary": "整体替换或清除待办提醒规则(上游不支持规则读回)",
|
||||
"use_when": [
|
||||
"需要清除或整体替换待办提醒规则时(可不传 reminder-rules 以清除);成功响应仅作为写入回执"
|
||||
"需要清除或整体替换待办提醒规则时(不传 reminder-rules 或传 [] 可清除);非空规则会在远端调用前严格校验,成功响应仅作为写入回执"
|
||||
],
|
||||
"avoid_when": [
|
||||
"只需追加一条提醒时改用 dws todo task add-reminder",
|
||||
@@ -606,12 +606,13 @@
|
||||
]
|
||||
},
|
||||
"todo.shortcut_remind": {
|
||||
"agent_summary": "给自己创建一条带截止/提醒时间的待办",
|
||||
"agent_summary": "给自己创建一条带可选截止时间的待办",
|
||||
"use_when": [
|
||||
"当你想给自己记一件事、并(可选)设一个截止/提醒时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 会按 ISO8601 解析为截止时间。会真实创建待办。"
|
||||
"当你想给自己记一件事、并(可选)设一个截止时间,又不想先查自己的 userId 时使用;内部先解析当前登录用户的 userId,再显式设置 executorIds,--at 只会按 ISO8601 写入截止时间 dueTime,不会创建独立提醒规则。会真实创建待办。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令",
|
||||
"用户需要独立提醒规则时改用 dws todo task add-reminder;不要把 --at 解释成提醒时间"
|
||||
],
|
||||
"examples": [
|
||||
"dws todo +remind --task \"交周报\" --at 2026-03-10T18:00:00+08:00"
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
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"
|
||||
@@ -52,26 +49,3 @@ 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": {},
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package helpers
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -15,13 +17,23 @@ import (
|
||||
)
|
||||
|
||||
type contractDefectCaller struct {
|
||||
dryRun bool
|
||||
calls []guardedMutationCall
|
||||
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})
|
||||
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: `{}`}}}, nil
|
||||
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" }
|
||||
@@ -85,42 +97,175 @@ func TestDocVersionRevertDryRunSkipsRemotePreflightAndEmitsPreview(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenameDocumentRemovesOnePreservedExtension(t *testing.T) {
|
||||
func TestDocRenamePreservesCallerProvidedDisplayName(t *testing.T) {
|
||||
caller := &contractDefectCaller{}
|
||||
if _, err := executeContractDefectCommand(t, caller, newDocCommand,
|
||||
"rename", "--node", "node-1", "--name", "release.v2"); err != nil {
|
||||
t.Fatalf("doc 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 != "release.v2" {
|
||||
t.Fatalf("newName = %#v, want release.v2", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriveRenameUsesNodeTypeAndCurrentExtension(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
build func() *cobra.Command
|
||||
args []string
|
||||
name string
|
||||
metadata string
|
||||
nodeID string
|
||||
wantFileID string
|
||||
requested string
|
||||
wantNewName string
|
||||
}{
|
||||
{
|
||||
name: "doc",
|
||||
build: newDocCommand,
|
||||
args: []string{"rename", "--node", "node-1", "--name", "report.txt"},
|
||||
name: "file strips matching extension outside old whitelist",
|
||||
metadata: `{"result":{"type":"file","extension":"heic"}}`,
|
||||
nodeID: "https://alidocs.dingtalk.com/i/nodes/node-1",
|
||||
wantFileID: "node-1",
|
||||
requested: "photo.HEIC",
|
||||
wantNewName: "photo",
|
||||
},
|
||||
{
|
||||
name: "drive",
|
||||
build: newDriveCommand,
|
||||
args: []string{"rename", "--node", "node-1", "--name", "report.txt"},
|
||||
name: "folder preserves dotted display name",
|
||||
metadata: `{"result":{"type":"folder","extension":"v2"}}`,
|
||||
requested: "release.v2",
|
||||
wantNewName: "release.v2",
|
||||
},
|
||||
{
|
||||
name: "different current extension is preserved",
|
||||
metadata: `{"result":{"nodeType":"file","fileExtension":"txt"}}`,
|
||||
requested: "report.final",
|
||||
wantNewName: "report.final",
|
||||
},
|
||||
}
|
||||
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)
|
||||
caller := &contractDefectCaller{responses: map[string]string{
|
||||
"drive/get_file_info": test.metadata,
|
||||
}}
|
||||
nodeID := test.nodeID
|
||||
if nodeID == "" {
|
||||
nodeID = "node-1"
|
||||
}
|
||||
if len(caller.calls) != 1 {
|
||||
t.Fatalf("tool calls = %#v, want one rename call", caller.calls)
|
||||
if _, err := executeContractDefectCommand(t, caller, newDriveCommand,
|
||||
"rename", "--node", nodeID, "--name", test.requested); err != nil {
|
||||
t.Fatalf("drive rename returned error: %v", err)
|
||||
}
|
||||
if got := caller.calls[0].args["newName"]; got != "report" {
|
||||
t.Fatalf("newName = %#v, want report", got)
|
||||
if len(caller.calls) != 2 {
|
||||
t.Fatalf("tool calls = %#v, want metadata read plus rename", caller.calls)
|
||||
}
|
||||
if caller.calls[0].productID != "drive" || caller.calls[0].toolName != "get_file_info" {
|
||||
t.Fatalf("first call = %#v, want drive/get_file_info", caller.calls[0])
|
||||
}
|
||||
wantFileID := test.wantFileID
|
||||
if wantFileID == "" {
|
||||
wantFileID = nodeID
|
||||
}
|
||||
if got := caller.calls[0].args["fileId"]; got != wantFileID {
|
||||
t.Fatalf("metadata fileId = %#v, want %q", got, wantFileID)
|
||||
}
|
||||
if caller.calls[1].productID != "doc" || caller.calls[1].toolName != "rename_document" {
|
||||
t.Fatalf("second call = %#v, want doc/rename_document", caller.calls[1])
|
||||
}
|
||||
if got := caller.calls[1].args["newName"]; got != test.wantNewName {
|
||||
t.Fatalf("newName = %#v, want %q", got, test.wantNewName)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if got := renameBaseName("release.v2"); got != "release.v2" {
|
||||
t.Fatalf("unknown dotted suffix = %q, want release.v2", got)
|
||||
func TestDriveRenameMetadataFailuresDoNotMutate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
responses map[string]string
|
||||
errors map[string]error
|
||||
}{
|
||||
{
|
||||
name: "metadata call error",
|
||||
errors: map[string]error{"drive/get_file_info": errors.New("metadata unavailable")},
|
||||
},
|
||||
{
|
||||
name: "invalid metadata JSON",
|
||||
responses: map[string]string{"drive/get_file_info": `{`},
|
||||
},
|
||||
{
|
||||
name: "metadata result missing",
|
||||
responses: map[string]string{"drive/get_file_info": `{}`},
|
||||
},
|
||||
}
|
||||
if got := renameBaseName("REPORT.XLSX"); got != "REPORT" {
|
||||
t.Fatalf("case-insensitive extension normalization = %q, want REPORT", got)
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
caller := &contractDefectCaller{responses: test.responses, errors: test.errors}
|
||||
_, err := executeContractDefectCommand(t, caller, newDriveCommand,
|
||||
"rename", "--node", "node-1", "--name", "report.txt")
|
||||
if err == nil {
|
||||
t.Fatal("drive rename returned nil error")
|
||||
}
|
||||
if len(caller.calls) != 1 || caller.calls[0].toolName != "get_file_info" {
|
||||
t.Fatalf("tool calls = %#v, want metadata read only", caller.calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriveRenameDryRunDefersMetadataNormalization(t *testing.T) {
|
||||
caller := &contractDefectCaller{dryRun: true}
|
||||
output, err := executeContractDefectCommand(t, caller, newDriveCommand,
|
||||
"rename", "--node", "node-1", "--name", "photo.heic", "--dry-run")
|
||||
if err != nil {
|
||||
t.Fatalf("drive rename 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, `"newName": "photo.heic"`) {
|
||||
t.Fatalf("dry-run output = %q, want unmodified input name", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriveRenameBaseNameConservativeEdges(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
nodeType string
|
||||
extension string
|
||||
want string
|
||||
}{
|
||||
{name: ".txt", nodeType: "file", extension: "txt", want: ".txt"},
|
||||
{name: "report.txt", nodeType: "directory", extension: "txt", want: "report.txt"},
|
||||
{name: "report.txt", nodeType: "dir", extension: "txt", want: "report.txt"},
|
||||
{name: " report.txt ", nodeType: "file", extension: "", want: "report.txt"},
|
||||
{name: "report.txt", nodeType: "file", extension: ".txt", want: "report"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := driveRenameBaseName(test.name, test.nodeType, test.extension); got != test.want {
|
||||
t.Fatalf("driveRenameBaseName(%q, %q, %q) = %q, want %q",
|
||||
test.name, test.nodeType, test.extension, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriveInfoRestoresFileSizeWhenDocMetadataIsNull(t *testing.T) {
|
||||
caller := &contractDefectCaller{responses: map[string]string{
|
||||
"drive/get_file_info": `{"result":{"fileId":"node-1","extension":"adoc","fileSize":93682}}`,
|
||||
"doc/get_document_info": `{"result":{"title":"Doc","fileSize":null}}`,
|
||||
}}
|
||||
output, err := executeContractDefectCommand(t, caller, newDriveCommand,
|
||||
"info", "--node", "node-1")
|
||||
if err != nil {
|
||||
t.Fatalf("drive info returned error: %v", err)
|
||||
}
|
||||
var payload struct {
|
||||
Result struct {
|
||||
FileSize int64 `json:"fileSize"`
|
||||
} `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(output), &payload); err != nil {
|
||||
t.Fatalf("decode drive info output %q: %v", output, err)
|
||||
}
|
||||
if payload.Result.FileSize != 93682 {
|
||||
t.Fatalf("fileSize = %d, want 93682", payload.Result.FileSize)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1548,7 +1548,7 @@ WARNING: --mode overwrite 为破坏性写入,会清空原文档全部内容。
|
||||
}
|
||||
return callMCPTool("rename_document", map[string]any{
|
||||
"nodeId": nodeID,
|
||||
"newName": renameBaseName(flagOrFallback(cmd, "name", "title")),
|
||||
"newName": flagOrFallback(cmd, "name", "title"),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -19,6 +19,51 @@ import (
|
||||
// get_upload_info, commit_upload
|
||||
// ──────────────────────────────────────────────────────────
|
||||
|
||||
func driveRenameBaseName(name, nodeType, currentExtension string) string {
|
||||
trimmed := strings.TrimSpace(name)
|
||||
switch strings.ToLower(strings.TrimSpace(nodeType)) {
|
||||
case "folder", "dir", "directory":
|
||||
return trimmed
|
||||
}
|
||||
|
||||
extension := strings.TrimLeft(strings.TrimSpace(currentExtension), ".")
|
||||
if extension == "" {
|
||||
return trimmed
|
||||
}
|
||||
suffix := "." + extension
|
||||
if len(trimmed) <= len(suffix) || !strings.EqualFold(trimmed[len(trimmed)-len(suffix):], suffix) {
|
||||
return trimmed
|
||||
}
|
||||
return trimmed[:len(trimmed)-len(suffix)]
|
||||
}
|
||||
|
||||
func resolveDriveRenameName(ctx context.Context, nodeID, name string) (string, error) {
|
||||
fileID := nodeID
|
||||
if parsedNodeID := extractNodeIDFromDocURL(nodeID); parsedNodeID != "" {
|
||||
fileID = parsedNodeID
|
||||
}
|
||||
text, err := callMCPToolReturnTextOnServer(ctx, "drive", "get_file_info", map[string]any{
|
||||
"fileId": fileID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("无法读取节点元数据,未执行重命名: %w", err)
|
||||
}
|
||||
|
||||
var response map[string]any
|
||||
if err := json.Unmarshal([]byte(text), &response); err != nil {
|
||||
return "", fmt.Errorf("无法解析节点元数据,未执行重命名: %w", err)
|
||||
}
|
||||
result, ok := response["result"].(map[string]any)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("节点元数据缺少 result,未执行重命名")
|
||||
}
|
||||
return driveRenameBaseName(
|
||||
name,
|
||||
firstStringField(result, "type", "nodeType", "fileType"),
|
||||
firstStringField(result, "extension", "fileExtension", "ext"),
|
||||
), nil
|
||||
}
|
||||
|
||||
func runDriveUpload(cmd *cobra.Command, _ []string) error {
|
||||
filePath := mustGetFlag(cmd, "file")
|
||||
if filePath == "" {
|
||||
@@ -834,8 +879,9 @@ func newDriveCommand() *cobra.Command {
|
||||
driveRenameCmd := &cobra.Command{
|
||||
Use: "rename",
|
||||
Short: "重命名文件/文档",
|
||||
Long: `修改文档空间中文档或文件的名称。服务端会保留节点的原扩展名;
|
||||
如果 --name 带常见文件扩展名,CLI 会自动去掉一层,避免出现 report.txt.txt。
|
||||
Long: `修改文档空间中文档、文件或文件夹的名称。
|
||||
实际执行前会读取节点类型和当前扩展名:文件的新名称仅在末尾扩展名与当前扩展名一致时去掉一层,
|
||||
避免 report.txt.txt;文件夹和扩展名不匹配的名称保持不变。dry-run 不读取远端元数据,因此保留输入名称。
|
||||
|
||||
权限要求: 对文档有"编辑"权限。`,
|
||||
Example: ` dws drive rename --node DOC_ID --name "新名称"`,
|
||||
@@ -847,14 +893,21 @@ func newDriveCommand() *cobra.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newName := flagOrFallback(cmd, "name", "title")
|
||||
if !commandDryRun(cmd) {
|
||||
newName, err = resolveDriveRenameName(cmd.Context(), nodeID, newName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return callMCPToolOnServer("doc", "rename_document", map[string]any{
|
||||
"nodeId": nodeID,
|
||||
"newName": renameBaseName(flagOrFallback(cmd, "name", "title")),
|
||||
"newName": newName,
|
||||
})
|
||||
},
|
||||
}
|
||||
driveRenameCmd.Flags().String("node", "", "文档/文件 ID 或 URL (必填)")
|
||||
driveRenameCmd.Flags().String("name", "", "新名称 (必填;服务端保留原扩展名,传入常见扩展名时 CLI 会自动去掉一层)")
|
||||
driveRenameCmd.Flags().String("name", "", "新名称 (必填;实际执行时仅去掉与节点当前扩展名完全匹配的一层后缀)")
|
||||
|
||||
driveStatsCmd := &cobra.Command{
|
||||
Use: "stats",
|
||||
@@ -1384,7 +1437,7 @@ func driveInfoWithDocFallback(fileID string, driveArgs map[string]any) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// Step 1: 调用 drive get_file_info
|
||||
driveText, err := callMCPToolReturnText(ctx, "get_file_info", driveArgs)
|
||||
driveText, err := callMCPToolReturnTextOnServer(ctx, "drive", "get_file_info", driveArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1445,7 +1498,8 @@ func driveInfoWithDocFallback(fileID string, driveArgs map[string]any) error {
|
||||
driveOnlyFields := []string{"dentryId", "path", "fileSize", "extension", "type"}
|
||||
for _, field := range driveOnlyFields {
|
||||
if val, ok := driveResult[field]; ok {
|
||||
if _, exists := docResult[field]; !exists {
|
||||
current, exists := docResult[field]
|
||||
if (!exists || current == nil) && val != nil {
|
||||
docResult[field] = val
|
||||
}
|
||||
}
|
||||
|
||||
+80
-25
@@ -481,7 +481,8 @@ reset-reminder 写入的提醒规则。提醒写命令的成功响应只能作
|
||||
todoTaskUpdateReminderCmd := &cobra.Command{
|
||||
Use: "reset-reminder",
|
||||
Short: "重置待办提醒",
|
||||
Long: `整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。
|
||||
Long: `整体替换待办提醒规则;不传 --reminder-rules 时清除提醒。显式传值必须是合法 JSON 数组,
|
||||
每条规则必须按 baseTime 提供 dueDateOffset 或 reminderTimeStamp;非法输入会在远端调用前失败。
|
||||
|
||||
当前上游没有提醒规则查询接口,task get/list 也不返回 reminderRules;
|
||||
成功响应是写入回执,不代表 CLI 能再次读取并核验最终规则。`,
|
||||
@@ -493,28 +494,14 @@ reset-reminder 写入的提醒规则。提醒写命令的成功响应只能作
|
||||
if err := validateRequiredFlags(cmd, "task-id"); err != nil {
|
||||
return err
|
||||
}
|
||||
var reminderRules []any
|
||||
if v, _ := cmd.Flags().GetString("reminder-rules"); v != "" {
|
||||
var att []any
|
||||
if err := json.Unmarshal([]byte(v), &att); err == nil && len(att) > 0 {
|
||||
for i, item := range att {
|
||||
m, ok := item.(map[string]any)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if m["baseTime"] == "customTime" {
|
||||
if ts, ok := m["reminderTimeStamp"].(string); ok && ts != "" {
|
||||
ms, err := parseISOTimeToMillis("reminderTimeStamp", ts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m["reminderTimeStamp"] = ms
|
||||
att[i] = m
|
||||
}
|
||||
}
|
||||
}
|
||||
reminderRules = att
|
||||
var reminderRules []map[string]any
|
||||
if cmd.Flags().Changed("reminder-rules") {
|
||||
value, _ := cmd.Flags().GetString("reminder-rules")
|
||||
parsedRules, err := parseTodoReminderRules(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reminderRules = parsedRules
|
||||
}
|
||||
return callMCPTool("reset_todo_reminder", map[string]any{
|
||||
"todoReminderUpdateRequest": map[string]any{
|
||||
@@ -686,7 +673,7 @@ reset-reminder 写入的提醒规则。提醒写命令的成功响应只能作
|
||||
todoTaskAddReminderCmd.Flags().String("reminder-time-stamp", "", "自定义提醒时间 ISO-8601 (如 2026-03-10T18:00:00+08:00,baseTime=customTime 时必填)")
|
||||
|
||||
todoTaskUpdateReminderCmd.Flags().String("task-id", "", "待办任务 ID (必填)")
|
||||
todoTaskUpdateReminderCmd.Flags().String("reminder-rules", "", "提醒规则 JSON 数组 (可选,为空则清除提醒)")
|
||||
todoTaskUpdateReminderCmd.Flags().String("reminder-rules", "", "提醒规则 JSON 数组 (不传则清除;显式传值必须合法)")
|
||||
todoTaskListSubCmd.Flags().String("task-id", "", "待办任务 ID (必填)")
|
||||
todoTaskListAttachmentCmd.Flags().String("task-id", "", "待办任务 ID (必填)")
|
||||
todoTaskAddAttachment.Flags().String("task-id", "", "待办任务 ID (必填)")
|
||||
@@ -934,13 +921,81 @@ func rejectUnsupportedTodoReminderFlags(cmd *cobra.Command) error {
|
||||
return nil
|
||||
}
|
||||
return &CLIError{
|
||||
Code: CodeMissingParam,
|
||||
Code: CodeInvalidParam,
|
||||
Message: fmt.Sprintf("todo 当前不支持独立 reminder 参数: %s", strings.Join(unsupported, ", ")),
|
||||
Suggestion: "请先用 --due 设置截止时间;需要独立提醒时,再用 dws todo task add-reminder。当前上游不支持读取提醒规则,不能用 task get/list 验证写入结果。",
|
||||
Suggestion: "需要截止时间请改用 --due;需要独立提醒请用 dws todo task add-reminder。dueTime 模式要求待办已有截止时间,customTime 模式直接传 --reminder-time-stamp,不必先设置 --due。当前上游不支持读取提醒规则,不能用 task get/list 验证写入结果。",
|
||||
Operation: "todo.task.reminder",
|
||||
}
|
||||
}
|
||||
|
||||
func parseTodoReminderRules(raw string) ([]map[string]any, error) {
|
||||
value := strings.TrimSpace(raw)
|
||||
if value == "" {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, "显式传入的 --reminder-rules 不能为空;清除提醒请省略该参数或传 []", nil)
|
||||
}
|
||||
if !json.Valid([]byte(value)) {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidJSON, "--reminder-rules 必须是合法 JSON 数组", nil)
|
||||
}
|
||||
|
||||
var rules []map[string]any
|
||||
if err := unmarshalJSONUseNumber(value, &rules); err != nil {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, "--reminder-rules 必须是由对象组成的 JSON 数组", err)
|
||||
}
|
||||
if rules == nil {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, "--reminder-rules 不能是 null;清除提醒请省略该参数或传 []", nil)
|
||||
}
|
||||
|
||||
for i, rule := range rules {
|
||||
position := i + 1
|
||||
if rule == nil {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, fmt.Sprintf("--reminder-rules 第 %d 条必须是对象", position), nil)
|
||||
}
|
||||
baseTime, ok := rule["baseTime"].(string)
|
||||
baseTime = strings.TrimSpace(baseTime)
|
||||
if !ok || baseTime == "" {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, fmt.Sprintf("--reminder-rules 第 %d 条缺少字符串 baseTime", position), nil)
|
||||
}
|
||||
rule["baseTime"] = baseTime
|
||||
|
||||
switch baseTime {
|
||||
case "dueTime":
|
||||
offset, ok := rule["dueDateOffset"].(json.Number)
|
||||
if !ok {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, fmt.Sprintf("--reminder-rules 第 %d 条在 baseTime=dueTime 时必须提供整数 dueDateOffset", position), nil)
|
||||
}
|
||||
parsed, err := strconv.ParseInt(offset.String(), 10, 64)
|
||||
if err != nil {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, fmt.Sprintf("--reminder-rules 第 %d 条的 dueDateOffset 必须是整数", position), err)
|
||||
}
|
||||
rule["dueDateOffset"] = parsed
|
||||
case "customTime":
|
||||
timestamp, ok := rule["reminderTimeStamp"].(string)
|
||||
timestamp = strings.TrimSpace(timestamp)
|
||||
if !ok || timestamp == "" {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, fmt.Sprintf("--reminder-rules 第 %d 条在 baseTime=customTime 时必须提供 ISO8601 字符串 reminderTimeStamp", position), nil)
|
||||
}
|
||||
parsed, err := parseISOTimeToMillis("reminderTimeStamp", timestamp)
|
||||
if err != nil {
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, fmt.Sprintf("--reminder-rules 第 %d 条的 reminderTimeStamp 无效", position), err)
|
||||
}
|
||||
rule["reminderTimeStamp"] = parsed
|
||||
default:
|
||||
return nil, invalidTodoReminderRules(CodeInvalidParam, fmt.Sprintf("--reminder-rules 第 %d 条的 baseTime 必须是 dueTime 或 customTime", position), nil)
|
||||
}
|
||||
}
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
func invalidTodoReminderRules(code, message string, cause error) error {
|
||||
return &CLIError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Suggestion: "使用 dueTime + 整数 dueDateOffset,或 customTime + ISO8601 reminderTimeStamp;清除提醒请省略 --reminder-rules 或传 []。",
|
||||
Operation: "todo.task.reset-reminder.reminder-rules",
|
||||
Cause: cause,
|
||||
}
|
||||
}
|
||||
|
||||
// todoListAutoPage 当 size > 20 时自动分页请求并合并结果。pageStr 为起始页码,wantSize 为期望条数。
|
||||
func todoListAutoPage(cmd *cobra.Command, pageStr string, wantSize int) error {
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -137,6 +137,8 @@ func TestCrossPlatformCoverageTodoUpdateReminderAndDetailEdges(t *testing.T) {
|
||||
{"task", "update", "--task-id", "1", "--due", "bad"},
|
||||
{"task", "add-reminder", "--task-id", "1", "--base-time", "customTime", "--reminder-time-stamp", "bad"},
|
||||
{"task", "reset-reminder", "--task-id", "1", "--reminder-rules", `[{"baseTime":"customTime","reminderTimeStamp":"bad"}]`},
|
||||
{"task", "reset-reminder", "--task-id", "1", "--reminder-rules", `not-json`},
|
||||
{"task", "reset-reminder", "--task-id", "1", "--reminder-rules", `[1,{"baseTime":"dueTime"},{"baseTime":"customTime","reminderTimeStamp":"` + validDate + `"}]`},
|
||||
}
|
||||
for _, args := range errorCases {
|
||||
if err := executeTodoEdge(t, &scriptedToolCaller{}, args...); err == nil {
|
||||
@@ -148,8 +150,6 @@ func TestCrossPlatformCoverageTodoUpdateReminderAndDetailEdges(t *testing.T) {
|
||||
{"task", "update", "--task-id", "1", "--priority", "bad"},
|
||||
{"task", "add-reminder", "--task-id", "1", "--base-time", "dueTime", "--due-date-offset", "-10"},
|
||||
{"task", "add-reminder", "--task-id", "1", "--base-time", "customTime", "--reminder-time-stamp", validDate},
|
||||
{"task", "reset-reminder", "--task-id", "1", "--reminder-rules", `not-json`},
|
||||
{"task", "reset-reminder", "--task-id", "1", "--reminder-rules", `[1,{"baseTime":"dueTime"},{"baseTime":"customTime","reminderTimeStamp":"` + validDate + `"}]`},
|
||||
}
|
||||
for _, args := range validCases {
|
||||
if err := executeTodoEdge(t, &scriptedToolCaller{}, args...); err != nil {
|
||||
|
||||
@@ -33,6 +33,10 @@ func (*todoReminderCaller) Fields() string { return "" }
|
||||
func (*todoReminderCaller) JQ() string { return "" }
|
||||
|
||||
func runTodoReminderCommand(t *testing.T, args ...string) (*todoReminderCaller, error) {
|
||||
return runTodoTaskCommandForReminderTests(t, "add-reminder", args...)
|
||||
}
|
||||
|
||||
func runTodoTaskCommandForReminderTests(t *testing.T, leaf string, args ...string) (*todoReminderCaller, error) {
|
||||
t.Helper()
|
||||
previousDeps := deps
|
||||
t.Cleanup(func() { deps = previousDeps })
|
||||
@@ -44,7 +48,7 @@ func runTodoReminderCommand(t *testing.T, args ...string) (*todoReminderCaller,
|
||||
cmd := newTodoCommand()
|
||||
cmd.SilenceErrors = true
|
||||
cmd.SilenceUsage = true
|
||||
cmd.SetArgs(append([]string{"task", "add-reminder"}, args...))
|
||||
cmd.SetArgs(append([]string{"task", leaf}, args...))
|
||||
return caller, cmd.Execute()
|
||||
}
|
||||
|
||||
@@ -137,6 +141,116 @@ func TestTodoAddReminderMapsReviewedModes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTodoResetReminderRejectsInvalidNonEmptyRulesBeforeCallingTool(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value string
|
||||
wantErr string
|
||||
}{
|
||||
{name: "explicit empty", value: "", wantErr: "不能为空"},
|
||||
{name: "whitespace", value: " ", wantErr: "不能为空"},
|
||||
{name: "malformed JSON", value: `[`, wantErr: "合法 JSON 数组"},
|
||||
{name: "object instead of array", value: `{}`, wantErr: "由对象组成的 JSON 数组"},
|
||||
{name: "scalar item", value: `[1]`, wantErr: "由对象组成的 JSON 数组"},
|
||||
{name: "null array", value: `null`, wantErr: "不能是 null"},
|
||||
{name: "null item", value: `[null]`, wantErr: "第 1 条必须是对象"},
|
||||
{name: "missing base time", value: `[{}]`, wantErr: "缺少字符串 baseTime"},
|
||||
{name: "non-string base time", value: `[{"baseTime":1}]`, wantErr: "缺少字符串 baseTime"},
|
||||
{name: "due time missing offset", value: `[{"baseTime":"dueTime"}]`, wantErr: "必须提供整数 dueDateOffset"},
|
||||
{name: "due time string offset", value: `[{"baseTime":"dueTime","dueDateOffset":"-30"}]`, wantErr: "必须提供整数 dueDateOffset"},
|
||||
{name: "due time fractional offset", value: `[{"baseTime":"dueTime","dueDateOffset":-1.5}]`, wantErr: "dueDateOffset 必须是整数"},
|
||||
{name: "custom time missing timestamp", value: `[{"baseTime":"customTime"}]`, wantErr: "必须提供 ISO8601 字符串"},
|
||||
{name: "custom time numeric timestamp", value: `[{"baseTime":"customTime","reminderTimeStamp":1}]`, wantErr: "必须提供 ISO8601 字符串"},
|
||||
{name: "custom time invalid timestamp", value: `[{"baseTime":"customTime","reminderTimeStamp":"tomorrow"}]`, wantErr: "reminderTimeStamp 无效"},
|
||||
{name: "unknown base time", value: `[{"baseTime":"deadline"}]`, wantErr: "baseTime 必须是 dueTime 或 customTime"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
caller, err := runTodoTaskCommandForReminderTests(t, "reset-reminder",
|
||||
"--task-id", "task-smoke", "--reminder-rules", test.value)
|
||||
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
|
||||
t.Fatalf("error = %v, want containing %q", err, test.wantErr)
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("tool calls = %#v, want none", caller.calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTodoResetReminderMapsValidatedRulesAndExplicitClears(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantRules []map[string]any
|
||||
}{
|
||||
{
|
||||
name: "omitted rules clear with null",
|
||||
args: []string{"--task-id", "task-smoke"},
|
||||
wantRules: nil,
|
||||
},
|
||||
{
|
||||
name: "explicit empty array clears with array",
|
||||
args: []string{"--task-id", "task-smoke", "--reminder-rules", `[]`},
|
||||
wantRules: []map[string]any{},
|
||||
},
|
||||
{
|
||||
name: "valid rules are normalized",
|
||||
args: []string{
|
||||
"--task-id", "task-smoke",
|
||||
"--reminder-rules",
|
||||
`[{"baseTime":"dueTime","dueDateOffset":-30},{"baseTime":"customTime","reminderTimeStamp":"2026-03-10T18:00:00+08:00","label":"keep"}]`,
|
||||
},
|
||||
wantRules: []map[string]any{
|
||||
{"baseTime": "dueTime", "dueDateOffset": int64(-30)},
|
||||
{"baseTime": "customTime", "reminderTimeStamp": int64(1773136800000), "label": "keep"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
caller, err := runTodoTaskCommandForReminderTests(t, "reset-reminder", test.args...)
|
||||
if err != nil {
|
||||
t.Fatalf("todo reset-reminder returned error: %v", err)
|
||||
}
|
||||
if len(caller.calls) != 1 || caller.calls[0].toolName != "reset_todo_reminder" {
|
||||
t.Fatalf("tool calls = %#v, want one reset_todo_reminder call", caller.calls)
|
||||
}
|
||||
request, ok := caller.calls[0].args["todoReminderUpdateRequest"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("request = %#v, want object", caller.calls[0].args["todoReminderUpdateRequest"])
|
||||
}
|
||||
if request["taskId"] != "task-smoke" {
|
||||
t.Fatalf("taskId = %#v, want task-smoke", request["taskId"])
|
||||
}
|
||||
rules, ok := request["reminderRules"].([]map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("reminderRules = %#v, want []map[string]any", request["reminderRules"])
|
||||
}
|
||||
if !reflect.DeepEqual(rules, test.wantRules) {
|
||||
t.Fatalf("reminderRules = %#v, want %#v", rules, test.wantRules)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnsupportedRemindAtGuidanceDoesNotRequireDueForCustomTime(t *testing.T) {
|
||||
caller, err := runTodoTaskCommandForReminderTests(t, "create",
|
||||
"--title", "x", "--executors", "user-1", "--remind-at", "2026-03-10T18:00:00+08:00")
|
||||
if err == nil {
|
||||
t.Fatal("todo create --remind-at returned nil error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "customTime") ||
|
||||
!strings.Contains(err.Error(), "不必先设置 --due") {
|
||||
t.Fatalf("error = %v, want customTime guidance without due requirement", err)
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("tool calls = %#v, want none", caller.calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTodoRoleTypesCSVExampleUsesRuntimeParser(t *testing.T) {
|
||||
got, err := parseRoleTypes("creator,executor")
|
||||
if err != nil {
|
||||
|
||||
@@ -40,6 +40,8 @@ func (f *platformCoverageCaller) CallTool(_ context.Context, product, tool strin
|
||||
switch product + "/" + tool {
|
||||
case "contact/search_contact_by_key_word":
|
||||
text = `{"result":[{"userId":"u1","name":"张三","openDingTalkId":"open1"}]}`
|
||||
case "contact/get_current_user_profile":
|
||||
text = `{"result":{"userId":"u1"}}`
|
||||
case "im/search_groups":
|
||||
text = `{"result":[{"openConversationId":"cid-1","title":"项目冲刺"}]}`
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package smart
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
)
|
||||
|
||||
func TestRemindShortcutDoesNotAdvertiseDueTimeAsReminder(t *testing.T) {
|
||||
@@ -18,3 +20,49 @@ func TestRemindShortcutDoesNotAdvertiseDueTimeAsReminder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemindShortcutWritesAtOnlyAsDueTime(t *testing.T) {
|
||||
fake := &platformCoverageCaller{}
|
||||
helpers.InitDeps(fake)
|
||||
root := newPlatformCoverageRoot()
|
||||
root.SetArgs([]string{
|
||||
"todo", "+remind",
|
||||
"--task", "交周报",
|
||||
"--at", "2026-03-10T18:00:00+08:00",
|
||||
"--yes",
|
||||
})
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatalf("execute +remind: %v", err)
|
||||
}
|
||||
if len(fake.calls) != 2 {
|
||||
t.Fatalf("tool calls = %#v, want profile read plus todo create", fake.calls)
|
||||
}
|
||||
call := fake.calls[1]
|
||||
if call.product != "todo" || call.tool != "create_personal_todo" {
|
||||
t.Fatalf("create call = %s/%s, want todo/create_personal_todo", call.product, call.tool)
|
||||
}
|
||||
request, ok := call.args["PersonalTodoCreateVO"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("PersonalTodoCreateVO = %#v, want object", call.args["PersonalTodoCreateVO"])
|
||||
}
|
||||
if got := request["dueTime"]; got != int64(1773136800000) {
|
||||
t.Fatalf("dueTime = %#v, want 1773136800000", got)
|
||||
}
|
||||
if _, exists := request["reminderRules"]; exists {
|
||||
t.Fatalf("unexpected reminderRules in %#v", request)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemindShortcutRejectsInvalidAtBeforeTodoCreate(t *testing.T) {
|
||||
fake := &platformCoverageCaller{}
|
||||
helpers.InitDeps(fake)
|
||||
root := newPlatformCoverageRoot()
|
||||
root.SetArgs([]string{"todo", "+remind", "--task", "交周报", "--at", "tomorrow", "--yes"})
|
||||
err := root.Execute()
|
||||
if err == nil || !strings.Contains(err.Error(), "--at 时间格式无效") {
|
||||
t.Fatalf("error = %v, want invalid --at validation", err)
|
||||
}
|
||||
if len(fake.calls) != 1 || fake.calls[0].tool != "get_current_user_profile" {
|
||||
t.Fatalf("tool calls = %#v, want profile read only", fake.calls)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -425,7 +425,7 @@ Flags:
|
||||
--name string 新名称 (仅 rename 必填)
|
||||
```
|
||||
|
||||
> **rename 只传主名,不要带扩展名**:服务端会按文件原扩展名自动补一个后缀。若 `--name` 里已带扩展名(如 `报告.txt`),回读会变成双扩展名 `报告.txt.txt`。正确做法:`dws drive rename --node <ID> --name "报告"`(不含 `.txt`),系统自动补回 `报告.txt`。
|
||||
> **rename 会按真实节点元数据处理扩展名**:实际执行前先读取节点类型和当前扩展名。文件的新名称若以当前扩展名结尾,只去掉完全匹配的一层再交给会保留原后缀的服务端;文件夹(包括 `release.v2` 这类带点名称)保持原样,也不再依赖扩展名白名单。`--dry-run` 不读取远端元数据,因此预览保留输入名称。
|
||||
|
||||
权限要求:copy 需对源文档有"阅读"权限且对目标文件夹有"编辑"权限;move 需对源文档有"管理"权限且对目标文件夹有"编辑"权限;rename 需对文档有"编辑"权限。
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ Example:
|
||||
dws todo task reset-reminder --task-id <taskId>
|
||||
dws todo task reset-reminder --task-id <taskId> --reminder-rules '[{"dueDateOffset":-30,"baseTime":"dueTime"},{"reminderTimeStamp":"2026-03-10T18:00:00+08:00","baseTime":"customTime"}]'
|
||||
Flags:
|
||||
--reminder-rules string 提醒规则 JSON 数组 (可选,为空则清除提醒)
|
||||
--reminder-rules string 提醒规则 JSON 数组 (不传则清除;显式传值必须合法)
|
||||
--task-id string 待办任务 ID (必填)
|
||||
```
|
||||
|
||||
@@ -291,6 +291,8 @@ JSON 数组,每个元素为一条提醒规则,支持两种 `baseTime` 模式
|
||||
```
|
||||
以上表示两条提醒规则:第一条在截止时间前 30 分钟提醒,第二条在指定时间(ISO-8601)提醒。
|
||||
|
||||
不传 `--reminder-rules` 或显式传 `[]` 表示清除提醒。其他显式值必须是合法对象数组:每条规则必须提供 `baseTime`;`dueTime` 必须带整数 `dueDateOffset`,`customTime` 必须带 ISO-8601 `reminderTimeStamp`。非法 JSON、`null`、标量元素或缺失模式字段都会在调用远端前报错,不会静默按 `null` 清空提醒。
|
||||
|
||||
### 给待办打标签
|
||||
```
|
||||
Usage:
|
||||
@@ -472,7 +474,8 @@ dws todo task list-sub --task-id <taskId> --format json
|
||||
- `task add-participant` / `task remove-participant` 用于管理待办的参与人,`--participants` 支持逗号分隔的多个 userId
|
||||
- 执行人 (executor) 与参与人 (participant) 的区别:执行人负责完成待办,参与人仅关注待办进度
|
||||
- `task add-reminder` 用于为待办添加提醒,`--base-time` 支持 `dueTime`(基于截止时间偏移,待办必须有截止时间)和 `customTime`(自定义时间戳)两种模式
|
||||
- `task reset-reminder` 用于重置待办提醒规则,不传 `--reminder-rules` 则清除所有提醒
|
||||
- `customTime` 直接使用 `--reminder-time-stamp`,不要求先设置 `--due`;只有 `dueTime` 模式依赖待办截止时间
|
||||
- `task reset-reminder` 用于重置待办提醒规则,不传 `--reminder-rules` 或传 `[]` 则清除所有提醒;其他值会严格校验,非法输入不会发出远端调用
|
||||
- `task add-attachment` / `list-attachment` / `remove-attachment` 三条附件命令均可用;`add-attachment` 会真实上传文件,勿用于试探性调用,先确认待办存在
|
||||
- 附件 ID 的取法:`add-attachment` 从 `result.attachmentIds[]` 取,`list-attachment` 从顶层 `attachments[].attachmentId` 取;`remove-attachment` 用 `--attachment-id` + `--yes`
|
||||
- 子待办 ID 只能从 `task list-sub` 的顶层 `subTasks[].taskId` 取;`task get` 的 `result.todoDetailModel.subTodos[]` 没有 taskId 字段
|
||||
|
||||
@@ -55,7 +55,7 @@ metadata:
|
||||
| "上传本地文件" | `dws drive upload --file ./report.pdf [--folder <fileId>]` |
|
||||
| "覆盖钉盘或知识库里的已有文件" | `dws drive upload --file ./updated.pdf --node <fileId> --dry-run`,确认后加 `--yes` |
|
||||
| "建文件夹" | `dws drive mkdir --name "<名称>" [--folder <fileId>]` |
|
||||
| "复制 / 移动 / 重命名" | `dws drive copy` / `move` / `rename --node <fileId> --name "<主名>"` |
|
||||
| "复制 / 移动 / 重命名" | `dws drive copy` / `move` / `rename --node <fileId> --name "<新名称>"` |
|
||||
| "删除文件 / 移到回收站(需确认)" | `dws drive delete --node <fileId> --yes` |
|
||||
| "回收站 / 还原" | `dws drive recycle list` / `recycle restore --id <recycleItemId>` |
|
||||
| "公开 / 取消公开 / 查公开状态" | `dws drive publish set` / `unset` / `get --node <fileId>` |
|
||||
@@ -65,7 +65,7 @@ metadata:
|
||||
- 找文件优先用 `drive search --query "<关键词>"`(不知道位置时全局搜);只有需要逐层浏览时才用 `drive list`。命中后必须 `drive info --node <fileId> --format json` 回读元数据。
|
||||
- **ID 字段选择**:`drive list` 返回同时有 `dentryId`(纯数字)和 `fileId`(UUID 格式)。所有 `--node` 和 `--folder` 参数**必须用 `fileId`**,纯数字 `dentryId` 会被拒绝。
|
||||
- `drive list` 默认 `--limit 20`,最大 50;要更多用 `--cursor` 翻页,不要因参数边界报错反复重试。
|
||||
- `rename` 的 `--name` **只传主名,不带扩展名**;服务端会按原扩展名自动补后缀,带了扩展名会变成双扩展名(如 `报告.txt` → `报告.txt.txt`)。
|
||||
- `rename` 实际执行前读取节点类型和当前扩展名;仅对非文件夹且末尾后缀与当前扩展名完全匹配的名称去掉一层,避免双扩展名。带点文件夹名保持原样;`--dry-run` 不读取元数据,预览保留输入名称。
|
||||
- `drive download` 需要 `--output` 指定本地保存路径或目录;不要省略必填输出位置。
|
||||
- 删除、覆盖、移动、公开(publish set/unset)等破坏性操作必须先确认;上传、创建文件夹、下载后要读回或列目录验证。
|
||||
- `drive upload --node <fileId>` 覆盖已有文件,`--node` 与新建文件使用的 `--folder` 互斥;先 `--dry-run`,获得明确确认后再加 `--yes`。
|
||||
|
||||
@@ -425,7 +425,7 @@ Flags:
|
||||
--name string 新名称 (仅 rename 必填)
|
||||
```
|
||||
|
||||
> **rename 只传主名,不要带扩展名**:服务端会按文件原扩展名自动补一个后缀。若 `--name` 里已带扩展名(如 `报告.txt`),回读会变成双扩展名 `报告.txt.txt`。正确做法:`dws drive rename --node <ID> --name "报告"`(不含 `.txt`),系统自动补回 `报告.txt`。
|
||||
> **rename 会按真实节点元数据处理扩展名**:实际执行前先读取节点类型和当前扩展名。文件的新名称若以当前扩展名结尾,只去掉完全匹配的一层再交给会保留原后缀的服务端;文件夹(包括 `release.v2` 这类带点名称)保持原样,也不再依赖扩展名白名单。`--dry-run` 不读取远端元数据,因此预览保留输入名称。
|
||||
|
||||
权限要求:copy 需对源文档有"阅读"权限且对目标文件夹有"编辑"权限;move 需对源文档有"管理"权限且对目标文件夹有"编辑"权限;rename 需对文档有"编辑"权限。
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ metadata:
|
||||
- `--id` / `--ids` 是隐藏兼容别名,文档和生成命令统一写 `--task-id`,减少模型漂移。
|
||||
- 优先级映射:低=10,普通=20,较高/高/重要=30,紧急/最高/P0/马上处理=40;不要把"较高"写成 40。
|
||||
- 截止时间必须是 ISO-8601。相对日期按当前日期计算;例如周五说"下周二"就是紧接下一个自然周的周二,不要再加一周。
|
||||
- 独立提醒使用 `task add-reminder`:`dueTime` 依赖待办已有截止时间,`customTime` 直接传 `--reminder-time-stamp`,不要求先设置 `--due`。隐藏兼容参数 `--remind-at` 不可用。
|
||||
- `task reset-reminder` 不传 `--reminder-rules` 或传 `[]` 表示清除;其他显式值必须是合法对象数组并通过模式字段校验,非法输入禁止发出远端调用。
|
||||
- 附件命令 `task add-attachment` / `list-attachment` / `remove-attachment` 均可用:`add-attachment --task-id <taskId> --file-path <本地文件>`(真实上传,先确认待办存在,返回 `result.attachmentIds`);`list-attachment --task-id <taskId>`(返回顶层 `attachments[].attachmentId`);`remove-attachment --task-id <taskId> --attachment-id <id> --yes`(不可逆,先确认)。
|
||||
- `tag add` 只是把标签关联到任务;`tag delete` 删除标签定义且不可恢复,必须先确认再加 `--yes`。`tag add` 最多传 2 个标签编码。
|
||||
- 创建、标记完成、重开、删除后必须 `task get` 或对应 `task list --status ...` 验证,不要只凭创建返回或口头计划结束。
|
||||
|
||||
@@ -267,7 +267,7 @@ Example:
|
||||
dws todo task reset-reminder --task-id <taskId>
|
||||
dws todo task reset-reminder --task-id <taskId> --reminder-rules '[{"dueDateOffset":-30,"baseTime":"dueTime"},{"reminderTimeStamp":"2026-03-10T18:00:00+08:00","baseTime":"customTime"}]'
|
||||
Flags:
|
||||
--reminder-rules string 提醒规则 JSON 数组 (可选,为空则清除提醒)
|
||||
--reminder-rules string 提醒规则 JSON 数组 (不传则清除;显式传值必须合法)
|
||||
--task-id string 待办任务 ID (必填)
|
||||
```
|
||||
|
||||
@@ -290,6 +290,8 @@ JSON 数组,每个元素为一条提醒规则,支持两种 `baseTime` 模式
|
||||
```
|
||||
以上表示两条提醒规则:第一条在截止时间前 30 分钟提醒,第二条在指定时间(ISO-8601)提醒。
|
||||
|
||||
不传 `--reminder-rules` 或显式传 `[]` 表示清除提醒。其他显式值必须是合法对象数组:每条规则必须提供 `baseTime`;`dueTime` 必须带整数 `dueDateOffset`,`customTime` 必须带 ISO-8601 `reminderTimeStamp`。非法 JSON、`null`、标量元素或缺失模式字段都会在调用远端前报错,不会静默按 `null` 清空提醒。
|
||||
|
||||
### 给待办打标签
|
||||
```
|
||||
Usage:
|
||||
@@ -470,7 +472,8 @@ dws todo task list-sub --task-id <taskId> --format json
|
||||
- `task add-participant` / `task remove-participant` 用于管理待办的参与人,`--participants` 支持逗号分隔的多个 userId
|
||||
- 执行人 (executor) 与参与人 (participant) 的区别:执行人负责完成待办,参与人仅关注待办进度
|
||||
- `task add-reminder` 用于为待办添加提醒,`--base-time` 支持 `dueTime`(基于截止时间偏移,待办必须有截止时间)和 `customTime`(自定义时间戳)两种模式
|
||||
- `task reset-reminder` 用于重置待办提醒规则,不传 `--reminder-rules` 则清除所有提醒
|
||||
- `customTime` 直接使用 `--reminder-time-stamp`,不要求先设置 `--due`;只有 `dueTime` 模式依赖待办截止时间
|
||||
- `task reset-reminder` 用于重置待办提醒规则,不传 `--reminder-rules` 或传 `[]` 则清除所有提醒;其他值会严格校验,非法输入不会发出远端调用
|
||||
- `task add-attachment` / `list-attachment` / `remove-attachment` 三条附件命令均可用;`add-attachment` 会真实上传文件,勿用于试探性调用,先确认待办存在
|
||||
- 附件 ID 的取法:`add-attachment` 从 `result.attachmentIds[]` 取,`list-attachment` 从顶层 `attachments[].attachmentId` 取;`remove-attachment` 用 `--attachment-id` + `--yes`
|
||||
- 子待办 ID 只能从 `task list-sub` 的顶层 `subTasks[].taskId` 取;`task get` 的 `result.todoDetailModel.subTodos[]` 没有 taskId 字段
|
||||
|
||||
Reference in New Issue
Block a user