Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b686efc23 | ||
|
|
9f5923736e | ||
|
|
dbe4b5f388 | ||
|
|
405a027002 | ||
|
|
09643fc6a1 | ||
|
|
a3e5145322 | ||
|
|
0e4ec9af34 | ||
|
|
9b62989ddb | ||
|
|
e81e040579 | ||
|
|
4f4fd4eb66 | ||
|
|
8510cb8b91 | ||
|
|
31cec9efa7 | ||
|
|
88f5a5a0e8 | ||
|
|
9dd8e232bb | ||
|
|
7094e5eceb | ||
|
|
e909f1083b |
@@ -15,11 +15,75 @@ package app
|
||||
|
||||
import (
|
||||
stderrors "errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
)
|
||||
|
||||
func TestChatGroupCreateMembersGuidanceUsesGenericExample(t *testing.T) {
|
||||
root := NewRootCommand()
|
||||
cmd := mustFindCommand(t, root, "chat", "group", "create")
|
||||
err := enrichChatWorkbookError(cmd, stderrors.New("unknown flag: --members"))
|
||||
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) {
|
||||
t.Fatalf("error = %T, want *errors.Error", err)
|
||||
}
|
||||
joined := strings.Join(typed.Examples, "\n")
|
||||
for _, unwanted := range []string{"V2评审小组", "489149", "550582"} {
|
||||
if strings.Contains(joined, unwanted) {
|
||||
t.Fatalf("example is fitted to evaluation data %q: %s", unwanted, joined)
|
||||
}
|
||||
}
|
||||
for _, want := range []string{"<群名称>", "<userId1>,<userId2>"} {
|
||||
if !strings.Contains(joined, want) {
|
||||
t.Fatalf("generic example missing %q: %s", want, joined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatHintPathsAreReportedAsUnknownSubcommandsBeforeFlagParsing(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
args []string
|
||||
message string
|
||||
flag string
|
||||
}{
|
||||
{name: "group search", args: []string{"chat", "group", "search", "--query", "1", "--format", "json"}, message: "chat group 下不存在 search 子命令", flag: "--query"},
|
||||
{name: "send", args: []string{"chat", "send", "--group", "cid", "--text", "hi", "--format", "json"}, message: "chat 下不存在 send 子命令", flag: "--group"},
|
||||
{name: "history", args: []string{"chat", "history", "--group", "cid", "--time", "2026-08-03 10:00:00", "--format", "json"}, message: "chat 下不存在 history 子命令", flag: "--group"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := validateChatWorkbookRawArgs(tc.args)
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) {
|
||||
t.Fatalf("error = %T, want *errors.Error", err)
|
||||
}
|
||||
if typed.Message != tc.message || typed.Reason != "unknown_subcommand" {
|
||||
t.Fatalf("guidance = %#v", typed)
|
||||
}
|
||||
if strings.Contains(typed.Message, tc.flag) || strings.Contains(typed.Reason, "unknown flag") {
|
||||
t.Fatalf("subcommand error was misreported as a flag error: %#v", typed)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
root := NewRootCommand()
|
||||
chat := mustFindCommand(t, root, "chat")
|
||||
for _, child := range chat.Commands() {
|
||||
if child.Name() == "send" || child.Name() == "history" {
|
||||
t.Fatalf("chat %s must not be registered as a hint subcommand", child.Name())
|
||||
}
|
||||
}
|
||||
group := mustFindCommand(t, root, "chat", "group")
|
||||
for _, child := range group.Commands() {
|
||||
if child.Name() == "search" {
|
||||
t.Fatal("chat group search must not be registered as a hint subcommand")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateChatWorkbookRawArgs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -82,6 +82,74 @@ func TestFlagErrorWithSuggestions_unknownFlagHintAndFlags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlagErrorWithSuggestionsDocParameterFamilies(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
path []string
|
||||
flag string
|
||||
wantHint string
|
||||
wantText string
|
||||
}{
|
||||
{"shortcut-create-file", []string{"doc", "+create"}, "content-file", "doc create", "--content-file"},
|
||||
{"inspect-info", []string{"doc", "+inspect"}, "include-info", "移除 --include-info", "默认返回"},
|
||||
{"inspect-versions", []string{"doc", "+inspect"}, "include-versions", "--include-history", "版本历史"},
|
||||
{"inspect-generic", []string{"doc", "+inspect"}, "include", "具体 --include-*", "block list"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
root := &cobra.Command{Use: "dws"}
|
||||
parent := root
|
||||
for _, name := range tc.path {
|
||||
child := &cobra.Command{Use: name}
|
||||
parent.AddCommand(child)
|
||||
parent = child
|
||||
}
|
||||
cmd := parent
|
||||
err := flagErrorWithSuggestions(cmd, fmt.Errorf("unknown flag: --%s", tc.flag))
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) {
|
||||
t.Fatalf("want structured validation, got %T: %v", err, err)
|
||||
}
|
||||
if typed.Reason != "doc_parameter_family_mismatch" || !strings.Contains(typed.Hint, tc.wantHint) || !strings.Contains(typed.Message, tc.wantText) {
|
||||
t.Fatalf("error = reason %q hint %q message %q", typed.Reason, typed.Hint, typed.Message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocParameterFamilyErrorsStopBeforeDispatch(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
path []string
|
||||
flag string
|
||||
}{
|
||||
{[]string{"doc", "+create"}, "content-file"},
|
||||
{[]string{"doc", "+inspect"}, "include-info"},
|
||||
{[]string{"doc", "+inspect"}, "include-versions"},
|
||||
{[]string{"doc", "+inspect"}, "include"},
|
||||
} {
|
||||
t.Run(strings.Join(tc.path, "/")+"/"+tc.flag, func(t *testing.T) {
|
||||
calls := 0
|
||||
root := &cobra.Command{Use: "dws", SilenceUsage: true, SilenceErrors: true}
|
||||
root.SetFlagErrorFunc(flagErrorWithSuggestions)
|
||||
parent := root
|
||||
for _, name := range tc.path {
|
||||
child := &cobra.Command{Use: name}
|
||||
parent.AddCommand(child)
|
||||
parent = child
|
||||
}
|
||||
parent.RunE = func(*cobra.Command, []string) error { calls++; return nil }
|
||||
root.SetArgs(append(tc.path, "--"+tc.flag))
|
||||
err := root.Execute()
|
||||
var typed *apperrors.Error
|
||||
if !stderrors.As(err, &typed) || typed.Reason != "doc_parameter_family_mismatch" {
|
||||
t.Fatalf("error = %#v", err)
|
||||
}
|
||||
if calls != 0 {
|
||||
t.Fatalf("dispatch calls = %d, want 0", calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestFlagErrorWithSuggestions_fallbackTailHint 验证 fallback 路径(非 unknown flag 类错误,
|
||||
// 如 missing required flag / ambiguous shorthand)也带尾部 See '<cmd> --help' for usage.
|
||||
// 这是 wukong / docker / kubectl 的通用 UX——任何 flag 解析错误都给用户一条 help 入口。
|
||||
|
||||
+80
-1
@@ -168,6 +168,27 @@ func suppressJSONDeprecationPreamble(root *cobra.Command, args []string) {
|
||||
func validateChatWorkbookRawArgs(args []string) error {
|
||||
path := strings.Join(args, " ")
|
||||
switch {
|
||||
case len(args) >= 3 && args[0] == "chat" && args[1] == "group" && args[2] == "search":
|
||||
return apperrors.NewValidation(
|
||||
"chat group 下不存在 search 子命令",
|
||||
apperrors.WithReason("unknown_subcommand"),
|
||||
apperrors.WithActions("群聊搜索使用 dws chat search,而不是 dws chat group search", "移除路径中的 group 后重试"),
|
||||
apperrors.WithExamples(`dws chat search --query <群名关键词> --format json`),
|
||||
)
|
||||
case len(args) >= 2 && args[0] == "chat" && args[1] == "send":
|
||||
return apperrors.NewValidation(
|
||||
"chat 下不存在 send 子命令",
|
||||
apperrors.WithReason("unknown_subcommand"),
|
||||
apperrors.WithActions("发送消息使用 dws chat message send,而不是 dws chat send", "在路径中补充 message 后重试"),
|
||||
apperrors.WithExamples(`dws chat message send --group <openConversationId> --text <消息正文> --format json`),
|
||||
)
|
||||
case len(args) >= 2 && args[0] == "chat" && args[1] == "history":
|
||||
return apperrors.NewValidation(
|
||||
"chat 下不存在 history 子命令",
|
||||
apperrors.WithReason("unknown_subcommand"),
|
||||
apperrors.WithActions("查询会话消息使用 dws chat message list,而不是 dws chat history", "改用 message list 并按帮助补充目标和时间参数"),
|
||||
apperrors.WithExamples(`dws chat message list --group <openConversationId> --time <YYYY-MM-DD HH:mm:ss> --format json`),
|
||||
)
|
||||
case strings.HasPrefix(path, "chat message send ") && rawArgsFlagValue(args, "msg-type") == "file" &&
|
||||
rawArgsContainFlag(args, "media-id"):
|
||||
return apperrors.NewValidation(
|
||||
@@ -441,7 +462,7 @@ func enrichChatWorkbookError(cmd *cobra.Command, err error) error {
|
||||
"建群命令不支持 --members",
|
||||
"chat group create 使用 --users 接收逗号分隔的成员 userId;--members 是其他命令的参数名",
|
||||
[]string{"将 --members 改为 --users", "成员标识不确定时先查询 userId"},
|
||||
[]string{`dws chat group create --name "V2评审小组" --users 489149,550582 --format json`},
|
||||
[]string{`dws chat group create --name "<群名称>" --users <userId1>,<userId2> --format json`},
|
||||
}
|
||||
case path == "chat group bots" && strings.Contains(message, "unknown flag: --id"):
|
||||
guide = chatWorkbookGuidance{
|
||||
@@ -600,6 +621,61 @@ func enrichChatWorkbookError(cmd *cobra.Command, err error) error {
|
||||
)
|
||||
}
|
||||
|
||||
// enrichDocFlagError turns common native/shortcut parameter-family mixups into
|
||||
// actionable local errors. It only uses the current Cobra command path and
|
||||
// parse error, so it always runs before any helper RunE or MCP dispatch.
|
||||
func enrichDocFlagError(cmd *cobra.Command, err error) error {
|
||||
if cmd == nil || err == nil {
|
||||
return err
|
||||
}
|
||||
path := cmd.CommandPath()
|
||||
if fields := strings.Fields(path); len(fields) > 1 {
|
||||
path = strings.Join(fields[1:], " ")
|
||||
}
|
||||
message := err.Error()
|
||||
var guide chatWorkbookGuidance
|
||||
switch {
|
||||
case path == "doc +create" && (strings.Contains(message, "unknown flag: --content-file") || strings.Contains(message, "unknown flag: --content-format")):
|
||||
guide = chatWorkbookGuidance{
|
||||
"文档创建命令与参数不属于同一组",
|
||||
"+create 是精简 Shortcut,不支持 --content-file/--content-format;需要从文件写入或指定 Markdown/JSONML 时应使用原生命令 doc create",
|
||||
[]string{"改用 dws doc create,并保留 --content-file/--content-format", "只创建短文本时才继续使用 +create 的 --content"},
|
||||
[]string{`dws doc create --name "<标题>" --content-file ./body.md --content-format markdown --format json`},
|
||||
}
|
||||
case path == "doc +inspect" && strings.Contains(message, "unknown flag: --include-info"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"+inspect 不需要 --include-info",
|
||||
"文档基本信息会由 +inspect 默认返回;移除 --include-info,只按需增加真实存在的 --include-history、--include-media、--include-comments、--include-permissions 或 --include-style",
|
||||
[]string{"移除 --include-info", "只保留任务需要的 --include-* 参数"},
|
||||
[]string{`dws doc +inspect --node <DOC_ID> --format json`},
|
||||
}
|
||||
case path == "doc +inspect" && strings.Contains(message, "unknown flag: --include-versions"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"+inspect 不支持 --include-versions",
|
||||
"版本历史对应的参数名是 --include-history;如果只需要版本列表,也可以使用 dws doc version list",
|
||||
[]string{"将 --include-versions 改为 --include-history", "只查询版本时使用 doc version list"},
|
||||
[]string{`dws doc +inspect --node <DOC_ID> --include-history --format json`, `dws doc version list --node <DOC_ID> --format json`},
|
||||
}
|
||||
case path == "doc +inspect" && strings.Contains(message, "unknown flag: --include"):
|
||||
guide = chatWorkbookGuidance{
|
||||
"+inspect 没有通用 --include 参数",
|
||||
"请直接使用具体开关:--include-history、--include-media、--include-comments、--include-permissions 或 --include-style;文档块请使用 doc block list",
|
||||
[]string{"把 --include <类型> 改成对应的具体 --include-* 开关", "需要 blockId 时改用 doc block list"},
|
||||
[]string{`dws doc +inspect --node <DOC_ID> --include-history --format json`, `dws doc block list --node <DOC_ID> --format json`},
|
||||
}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
return apperrors.NewValidation(
|
||||
guide.message+": "+guide.reason,
|
||||
apperrors.WithReason("doc_parameter_family_mismatch"),
|
||||
apperrors.WithHint(guide.actions[0]),
|
||||
apperrors.WithActions(guide.actions...),
|
||||
apperrors.WithExamples(guide.examples...),
|
||||
apperrors.WithCause(err),
|
||||
)
|
||||
}
|
||||
|
||||
// newPreParseValidationError keeps pipeline handler identity in internal logs
|
||||
// while exposing only the underlying parameter-domain error to CLI users.
|
||||
func newPreParseValidationError(err error) error {
|
||||
@@ -682,6 +758,9 @@ func flagErrorWithSuggestions(cmd *cobra.Command, err error) error {
|
||||
if enriched := enrichChatWorkbookError(cmd, err); enriched != err {
|
||||
return enriched
|
||||
}
|
||||
if enriched := enrichDocFlagError(cmd, err); enriched != err {
|
||||
return enriched
|
||||
}
|
||||
|
||||
// Common flag aliases and suggestions
|
||||
suggestions := map[string]string{
|
||||
|
||||
@@ -90,6 +90,56 @@ func TestChatAgentGuidanceRendersOnlyOnStdout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateAlignmentChatGuidance(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
wants []string
|
||||
}{
|
||||
{[]string{"chat", "message", "query-send-status", "--help"}, []string{"sendStatus=SUCCESS", "FAILED"}},
|
||||
{[]string{"chat", "+messages-query-send-status", "--help"}, []string{"sendStatus=SUCCESS", "openTaskId"}},
|
||||
{[]string{"chat", "message", "set-pin-msg", "--help"}, []string{"openTaskId", "set-top-msg", "chat set-top"}},
|
||||
{[]string{"chat", "message", "unset-pin-msg", "--help"}, []string{"复用", "unset-top-msg"}},
|
||||
{[]string{"chat", "message", "add-emoji", "--help"}, []string{"同一条真实消息", "openTaskId"}},
|
||||
{[]string{"chat", "message", "remove-emoji", "--help"}, []string{"复用", "表情名称"}},
|
||||
{[]string{"chat", "group", "members", "remove", "--help"}, []string{"群主", "转让群主"}},
|
||||
{[]string{"chat", "group", "update-icon", "--help"}, []string{"dentryId", "能力边界"}},
|
||||
{[]string{"chat", "group", "update-settings", "--help"}, []string{"群级设置", "user-settings set"}},
|
||||
{[]string{"chat", "group", "user-settings", "query", "--help"}, []string{"当前用户视角", "保存原值"}},
|
||||
{[]string{"chat", "group", "user-settings", "set", "--help"}, []string{"再次 query", "真实值恢复"}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
cmd := NewRootCommand()
|
||||
var stdout bytes.Buffer
|
||||
cmd.SetOut(&stdout)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs(tc.args)
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("%v: %v", tc.args, err)
|
||||
}
|
||||
for _, want := range tc.wants {
|
||||
if !strings.Contains(stdout.String(), want) {
|
||||
t.Fatalf("%v help missing %q:\n%s", tc.args, want, stdout.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCandidateAlignmentDriveUploadSchemaGuidance(t *testing.T) {
|
||||
cmd := NewRootCommand()
|
||||
var stdout bytes.Buffer
|
||||
cmd.SetOut(&stdout)
|
||||
cmd.SetErr(io.Discard)
|
||||
cmd.SetArgs([]string{"schema", "drive.upload", "--format", "json"})
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("drive.upload schema: %v", err)
|
||||
}
|
||||
for _, want := range []string{"暂时不要发送", "不会发送聊天消息"} {
|
||||
if !strings.Contains(stdout.String(), want) {
|
||||
t.Fatalf("drive.upload schema missing %q:\n%s", want, stdout.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootKeepsMainBranchChatCompatibilityCommands(t *testing.T) {
|
||||
root := NewRootCommand()
|
||||
listDirect := mustFindCommand(t, root, "chat", "message", "list-direct")
|
||||
|
||||
@@ -1099,7 +1099,7 @@ var generatedParamAliases = []ParamAliasEntry{
|
||||
"open-conversation-ids": "conversation-ids",
|
||||
"user-ids": "users",
|
||||
},
|
||||
Blocked: []string{"at-user-ids", "chat-id", "conversation-id", "group-id", "group-ids", "open-conversation-id", "staff-id", "uid", "userid"},
|
||||
Blocked: []string{"at-user-ids", "chat-id", "group-id", "group-ids", "open-conversation-id", "staff-id", "uid", "userid"},
|
||||
},
|
||||
{
|
||||
CLIPath: "chat message send",
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -308,7 +308,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -317,7 +317,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -525,7 +525,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -568,7 +568,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -577,7 +577,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -1815,7 +1815,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -1858,7 +1858,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -1867,7 +1867,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -3624,7 +3624,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -3667,7 +3667,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -3676,7 +3676,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -4659,7 +4659,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -4702,7 +4702,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -4711,7 +4711,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -4877,7 +4877,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -4886,7 +4886,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -4909,7 +4909,7 @@
|
||||
"internal/cli/schema_hints/selection/aitable.json"
|
||||
],
|
||||
"use_when": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
]
|
||||
},
|
||||
"aitable +resolve-table": {
|
||||
@@ -4917,7 +4917,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -4960,7 +4960,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -4969,7 +4969,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -5135,7 +5135,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -5144,7 +5144,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -5167,7 +5167,7 @@
|
||||
"internal/cli/schema_hints/selection/aitable.json"
|
||||
],
|
||||
"use_when": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
]
|
||||
},
|
||||
"aitable +role-list": {
|
||||
@@ -5949,7 +5949,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -5993,7 +5993,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -6002,7 +6002,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -11191,11 +11191,11 @@
|
||||
]
|
||||
},
|
||||
"aitable base search": {
|
||||
"agent_summary": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。",
|
||||
"agent_summary": "底层按名称搜索 AI 表格 Base,返回原始候选列表。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -11205,14 +11205,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。",
|
||||
"value": "底层按名称搜索 AI 表格 Base,返回原始候选列表。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。",
|
||||
"value": "底层按名称搜索 AI 表格 Base,返回原始候选列表。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -11244,7 +11244,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -11253,7 +11253,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -11407,7 +11407,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -11416,7 +11416,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -11441,10 +11441,12 @@
|
||||
"internal/cli/schema_hints/metadata/aitable.json",
|
||||
"internal/cli/schema_hints/selection/aitable.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.base_search",
|
||||
"skills/mono/references/products/aitable.md"
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md"
|
||||
],
|
||||
"use_when": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
]
|
||||
},
|
||||
"aitable base update": {
|
||||
@@ -16699,11 +16701,11 @@
|
||||
]
|
||||
},
|
||||
"aitable field get": {
|
||||
"agent_summary": "获取字段完整配置。",
|
||||
"agent_summary": "底层获取字段完整类型与 config。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -16713,14 +16715,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "获取字段完整配置。",
|
||||
"value": "底层获取字段完整类型与 config。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "获取字段完整配置。",
|
||||
"value": "底层获取字段完整类型与 config。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -16752,7 +16754,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -16761,7 +16763,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -16913,7 +16915,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -16922,7 +16924,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -16950,10 +16952,12 @@
|
||||
"internal/cli/schema_hints/selection/aitable.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.field_get",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-field.md"
|
||||
"skills/mono/references/products/aitable/aitable-field.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-field.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
"aitable field list": {
|
||||
@@ -22339,11 +22343,11 @@
|
||||
]
|
||||
},
|
||||
"aitable record create": {
|
||||
"agent_summary": "新增记录(cells 的 key 必须是 fieldId)。",
|
||||
"agent_summary": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -22353,14 +22357,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "新增记录(cells 的 key 必须是 fieldId)。",
|
||||
"value": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "新增记录(cells 的 key 必须是 fieldId)。",
|
||||
"value": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -22392,7 +22396,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -22401,7 +22405,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -22553,7 +22557,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -22562,7 +22566,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -22592,10 +22596,12 @@
|
||||
"skills/mono/references/products/aitable-record-ops.md",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-attachment.md",
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md"
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-create.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
]
|
||||
},
|
||||
"aitable record delete": {
|
||||
@@ -23929,11 +23935,11 @@
|
||||
]
|
||||
},
|
||||
"aitable record query": {
|
||||
"agent_summary": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。",
|
||||
"agent_summary": "底层查询/搜索记录,额外支持原子命令的完整分页控制。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -23943,14 +23949,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。",
|
||||
"value": "底层查询/搜索记录,额外支持原子命令的完整分页控制。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。",
|
||||
"value": "底层查询/搜索记录,额外支持原子命令的完整分页控制。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -23982,7 +23988,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -23991,7 +23997,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24151,7 +24157,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24160,7 +24166,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24189,10 +24195,12 @@
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.query_records",
|
||||
"skills/mono/references/products/aitable-record-ops.md",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-record-query.md"
|
||||
"skills/mono/references/products/aitable/aitable-record-query.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-query.md"
|
||||
],
|
||||
"use_when": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
"aitable record query-empty": {
|
||||
@@ -24711,11 +24719,11 @@
|
||||
]
|
||||
},
|
||||
"aitable record update": {
|
||||
"agent_summary": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。",
|
||||
"agent_summary": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -24725,14 +24733,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。",
|
||||
"value": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。",
|
||||
"value": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -24764,7 +24772,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24773,7 +24781,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24925,7 +24933,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24934,7 +24942,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24963,10 +24971,12 @@
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.record_update",
|
||||
"skills/mono/references/products/aitable-record-ops.md",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-attachment.md"
|
||||
"skills/mono/references/products/aitable/aitable-attachment.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-update.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
]
|
||||
},
|
||||
"aitable record upsert": {
|
||||
@@ -27862,11 +27872,11 @@
|
||||
]
|
||||
},
|
||||
"aitable table get": {
|
||||
"agent_summary": "获取数据表结构(字段+视图目录)。",
|
||||
"agent_summary": "底层获取数据表结构、精简字段目录与视图目录。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -27876,14 +27886,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "获取数据表结构(字段+视图目录)。",
|
||||
"value": "底层获取数据表结构、精简字段目录与视图目录。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "获取数据表结构(字段+视图目录)。",
|
||||
"value": "底层获取数据表结构、精简字段目录与视图目录。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -27915,7 +27925,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -27924,7 +27934,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -28076,7 +28086,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -28085,7 +28095,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -28115,10 +28125,12 @@
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-advperm.md",
|
||||
"skills/mono/references/products/aitable/aitable-primary-doc.md",
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md"
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
"aitable table list": {
|
||||
|
||||
@@ -9301,11 +9301,11 @@
|
||||
]
|
||||
},
|
||||
"chat +messages-query-send-status": {
|
||||
"agent_summary": "查询消息发送状态",
|
||||
"agent_summary": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -9315,14 +9315,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "查询消息发送状态",
|
||||
"value": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功",
|
||||
"source": "internal/cli/schema_hints/selection/chat.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": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -9348,7 +9348,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -9357,7 +9357,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -9523,7 +9523,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -9532,7 +9532,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -9555,7 +9555,7 @@
|
||||
"internal/cli/schema_hints/selection/chat.json"
|
||||
],
|
||||
"use_when": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
]
|
||||
},
|
||||
"chat +messages-read-status": {
|
||||
@@ -20567,7 +20567,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -20616,7 +20616,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -20625,7 +20625,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -20804,7 +20804,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -20813,7 +20813,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -20844,7 +20844,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
]
|
||||
},
|
||||
"chat group members remove-bot": {
|
||||
@@ -24064,7 +24064,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -24113,7 +24113,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24122,7 +24122,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24268,7 +24268,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24277,7 +24277,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24306,7 +24306,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
]
|
||||
},
|
||||
"chat group update-nick": {
|
||||
@@ -24602,7 +24602,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -24651,7 +24651,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24660,7 +24660,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24806,7 +24806,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24815,7 +24815,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -24844,7 +24844,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
]
|
||||
},
|
||||
"chat group upgrade-to-external": {
|
||||
@@ -25112,7 +25112,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -25155,7 +25155,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25164,7 +25164,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25330,7 +25330,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25339,7 +25339,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25362,7 +25362,7 @@
|
||||
"wukong-develop:wukong/products"
|
||||
],
|
||||
"use_when": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
]
|
||||
},
|
||||
"chat group user-settings set": {
|
||||
@@ -25370,7 +25370,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -25413,7 +25413,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25422,7 +25422,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25588,7 +25588,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25597,7 +25597,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -25620,7 +25620,7 @@
|
||||
"wukong-develop:wukong/products"
|
||||
],
|
||||
"use_when": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
]
|
||||
},
|
||||
"chat group-mute": {
|
||||
@@ -29273,11 +29273,11 @@
|
||||
]
|
||||
},
|
||||
"chat message add-emoji": {
|
||||
"agent_summary": "给指定消息添加表情回应",
|
||||
"agent_summary": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -29287,14 +29287,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "给指定消息添加表情回应",
|
||||
"value": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "给指定消息添加表情回应",
|
||||
"value": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -29326,7 +29326,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -29335,7 +29335,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -29489,7 +29489,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -29498,7 +29498,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -29526,7 +29526,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
]
|
||||
},
|
||||
"chat message add-favorite": {
|
||||
@@ -34634,11 +34634,11 @@
|
||||
]
|
||||
},
|
||||
"chat message query-send-status": {
|
||||
"agent_summary": "查询异步消息发送任务的状态",
|
||||
"agent_summary": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "read",
|
||||
@@ -34648,14 +34648,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "查询异步消息发送任务的状态",
|
||||
"value": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "查询异步消息发送任务的状态",
|
||||
"value": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -34687,7 +34687,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -34696,7 +34696,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -34842,7 +34842,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -34851,7 +34851,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -34880,7 +34880,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
]
|
||||
},
|
||||
"chat message read-status": {
|
||||
@@ -35691,11 +35691,11 @@
|
||||
]
|
||||
},
|
||||
"chat message remove-emoji": {
|
||||
"agent_summary": "移除指定消息上的表情回应",
|
||||
"agent_summary": "从同一条真实消息移除此前添加的同名表情回应",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -35705,14 +35705,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "移除指定消息上的表情回应",
|
||||
"value": "从同一条真实消息移除此前添加的同名表情回应",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "移除指定消息上的表情回应",
|
||||
"value": "从同一条真实消息移除此前添加的同名表情回应",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -35744,7 +35744,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -35753,7 +35753,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -35907,7 +35907,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -35916,7 +35916,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -35944,7 +35944,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
]
|
||||
},
|
||||
"chat message remove-favorite": {
|
||||
@@ -38319,11 +38319,11 @@
|
||||
]
|
||||
},
|
||||
"chat message set-pin-msg": {
|
||||
"agent_summary": "把指定消息设为会话置顶消息",
|
||||
"agent_summary": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -38333,14 +38333,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "把指定消息设为会话置顶消息",
|
||||
"value": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "把指定消息设为会话置顶消息",
|
||||
"value": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -38372,7 +38372,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -38381,7 +38381,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -38527,7 +38527,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -38536,7 +38536,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -38565,7 +38565,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
]
|
||||
},
|
||||
"chat message set-top-msg": {
|
||||
@@ -38854,11 +38854,11 @@
|
||||
]
|
||||
},
|
||||
"chat message unset-pin-msg": {
|
||||
"agent_summary": "取消指定消息的会话置顶",
|
||||
"agent_summary": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
],
|
||||
"confirmation": "not_required",
|
||||
"effect": "write",
|
||||
@@ -38868,14 +38868,14 @@
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "取消指定消息的会话置顶",
|
||||
"value": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "取消指定消息的会话置顶",
|
||||
"value": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
@@ -38907,7 +38907,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -38916,7 +38916,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -39062,7 +39062,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -39071,7 +39071,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -39100,7 +39100,7 @@
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
]
|
||||
},
|
||||
"chat message unset-top-msg": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10478,8 +10478,10 @@
|
||||
"agent_summary_source": "dws-agent-selection/drive",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
],
|
||||
@@ -10496,14 +10498,14 @@
|
||||
"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 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "上传本地文件到钉盘或文档空间,或按节点 ID 确认覆盖已有文件",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。"
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -10525,27 +10527,31 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
],
|
||||
"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 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。"
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -10601,7 +10607,7 @@
|
||||
"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 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
@@ -10611,7 +10617,7 @@
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。"
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -10704,7 +10710,7 @@
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": false,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。"
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -10733,24 +10739,26 @@
|
||||
"use_when": {
|
||||
"value": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
],
|
||||
"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 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。"
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10774,7 +10782,8 @@
|
||||
],
|
||||
"use_when": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:eedf9163f496888aa3a065bdbd85647e3702a73ce74703f27083059e22a74098",
|
||||
"source_hash": "sha256:35727a6c324fbffde5271e96d972843abdf780391151ba858f6d04384268cd51",
|
||||
"surface_hash": "sha256:4897d582a472d9d3596f88fb4546e7795d6b9bc334d020712f5d895ce894f7a7",
|
||||
"coverage": {
|
||||
"surface_products": 26,
|
||||
@@ -12,8 +12,8 @@
|
||||
"tools_with_avoid_when": 875,
|
||||
"tools_with_examples": 875,
|
||||
"tools_with_interface_mode": 875,
|
||||
"unmatched_skill_tools": 96,
|
||||
"unreviewed_skill_tools": 11
|
||||
"unmatched_skill_tools": 97,
|
||||
"unreviewed_skill_tools": 12
|
||||
},
|
||||
"products": {
|
||||
"aisearch": {
|
||||
@@ -88,20 +88,20 @@
|
||||
]
|
||||
},
|
||||
"aitable": {
|
||||
"agent_summary": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"agent_summary": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"avoid_when": [
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
],
|
||||
"field_provenance": {
|
||||
"agent_summary": {
|
||||
"value": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"value": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"candidates": [
|
||||
{
|
||||
"value": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"value": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true
|
||||
@@ -110,7 +110,7 @@
|
||||
},
|
||||
"avoid_when": {
|
||||
"value": [
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -118,7 +118,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -128,7 +128,7 @@
|
||||
},
|
||||
"use_when": {
|
||||
"value": [
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -136,7 +136,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"value": [
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
],
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"precedence": "reviewed_explicit",
|
||||
@@ -153,10 +153,11 @@
|
||||
"internal/cli/schema_hints/selection/aitable.json",
|
||||
"skills/mono/SKILL.md",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/aitable.md"
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md"
|
||||
],
|
||||
"use_when": [
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
]
|
||||
},
|
||||
"attendance": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"source_hash": "sha256:eedf9163f496888aa3a065bdbd85647e3702a73ce74703f27083059e22a74098",
|
||||
"source_hash": "sha256:35727a6c324fbffde5271e96d972843abdf780391151ba858f6d04384268cd51",
|
||||
"surface_hash": "sha256:4897d582a472d9d3596f88fb4546e7795d6b9bc334d020712f5d895ce894f7a7",
|
||||
"source_files": 160,
|
||||
"hint_files": 54,
|
||||
@@ -36,8 +36,8 @@
|
||||
"tools_with_avoid_when": 875,
|
||||
"tools_with_examples": 875,
|
||||
"tools_with_interface_mode": 875,
|
||||
"unmatched_skill_tools": 96,
|
||||
"unreviewed_skill_tools": 11
|
||||
"unmatched_skill_tools": 97,
|
||||
"unreviewed_skill_tools": 12
|
||||
},
|
||||
"source_products": [
|
||||
"agoal",
|
||||
@@ -1301,6 +1301,30 @@
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc drive upload",
|
||||
"source": "skills/mono/references/products/doc.md",
|
||||
"line": 668,
|
||||
"candidates": [
|
||||
"doc upload",
|
||||
"drive upload",
|
||||
"doc +comment-create"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc.md",
|
||||
"line": 669,
|
||||
"candidates": [
|
||||
"doc import get",
|
||||
"doc +comment-create",
|
||||
"doc +comment-list"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc.md",
|
||||
@@ -1315,6 +1339,30 @@
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc drive upload",
|
||||
"source": "skills/mono/references/products/doc.md",
|
||||
"line": 745,
|
||||
"candidates": [
|
||||
"doc upload",
|
||||
"drive upload",
|
||||
"doc +comment-create"
|
||||
]
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc.md",
|
||||
"line": 746,
|
||||
"candidates": [
|
||||
"doc import get",
|
||||
"doc +comment-create",
|
||||
"doc +comment-list"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc.md",
|
||||
@@ -1357,34 +1405,6 @@
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc/doc-import.md",
|
||||
"line": 12,
|
||||
"candidates": [
|
||||
"doc import get",
|
||||
"doc +comment-create",
|
||||
"doc +comment-list"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc/doc-import.md",
|
||||
"line": 13,
|
||||
"candidates": [
|
||||
"doc import get",
|
||||
"doc +comment-create",
|
||||
"doc +comment-list"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc/doc-import.md",
|
||||
@@ -1413,6 +1433,34 @@
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc/doc-import.md",
|
||||
"line": 16,
|
||||
"candidates": [
|
||||
"doc import get",
|
||||
"doc +comment-create",
|
||||
"doc +comment-list"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "doc import",
|
||||
"source": "skills/mono/references/products/doc/doc-import.md",
|
||||
"line": 17,
|
||||
"candidates": [
|
||||
"doc import get",
|
||||
"doc +comment-create",
|
||||
"doc +comment-list"
|
||||
],
|
||||
"review": {
|
||||
"status": "stale",
|
||||
"reason": "旧版 Skill 或说明性引用,当前公开命令面无等价 leaf,禁止词法模糊映射"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tool_path": "event consume user_im_message_receive_o2o_all",
|
||||
"source": "skills/mono/references/products/event.md",
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"version": 1,
|
||||
"surface_hash": "sha256:4897d582a472d9d3596f88fb4546e7795d6b9bc334d020712f5d895ce894f7a7",
|
||||
"source_hash": "sha256:113dbc3507c5e4d871802d567e960c4947b8066472c28fb8a6dc64e9fef36da9",
|
||||
"source_hash": "sha256:807d11cf6c62a0d4aaca4b5a6b438e6654b2792d3a32b4e271032202d45ff8fa",
|
||||
"catalog": {
|
||||
"agent_metadata": {
|
||||
"products_with_metadata": 26,
|
||||
"source": "embedded-skill-metadata",
|
||||
"source_hash": "sha256:eedf9163f496888aa3a065bdbd85647e3702a73ce74703f27083059e22a74098",
|
||||
"source_hash": "sha256:35727a6c324fbffde5271e96d972843abdf780391151ba858f6d04384268cd51",
|
||||
"surface_hash": "sha256:4897d582a472d9d3596f88fb4546e7795d6b9bc334d020712f5d895ce894f7a7",
|
||||
"surface_products": 26,
|
||||
"surface_tools": 875,
|
||||
"tools_with_agent_summary": 875,
|
||||
"tools_with_metadata": 875,
|
||||
"unmatched_skill_tools": 96,
|
||||
"unmatched_skill_tools": 97,
|
||||
"version": 1
|
||||
},
|
||||
"count": 26,
|
||||
@@ -225,12 +225,13 @@
|
||||
"internal/cli/schema_hints/selection/aitable.json",
|
||||
"skills/mono/SKILL.md",
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/aitable.md"
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md"
|
||||
],
|
||||
"agent_summary": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"agent_summary": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"avoid_when": [
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
],
|
||||
"description": "AI 表格操作",
|
||||
"field_provenance": {
|
||||
@@ -240,13 +241,13 @@
|
||||
"precedence": "reviewed_explicit",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。"
|
||||
"value": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。"
|
||||
"value": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。"
|
||||
},
|
||||
"avoid_when": {
|
||||
"candidates": [
|
||||
@@ -255,7 +256,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -263,7 +264,7 @@
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
]
|
||||
},
|
||||
"use_when": {
|
||||
@@ -273,7 +274,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -281,7 +282,7 @@
|
||||
"resolution": "highest_precedence",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -726,11 +727,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。",
|
||||
"agent_summary": "底层按名称搜索 AI 表格 Base,返回原始候选列表。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
],
|
||||
"canonical_path": "aitable.base_search",
|
||||
"cli_name": "search",
|
||||
@@ -752,7 +753,7 @@
|
||||
"risk": "low",
|
||||
"title": "搜索 AI 表格",
|
||||
"use_when": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1375,11 +1376,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "获取字段完整配置。",
|
||||
"agent_summary": "底层获取字段完整类型与 config。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
],
|
||||
"canonical_path": "aitable.field_get",
|
||||
"cli_name": "get",
|
||||
@@ -1401,7 +1402,7 @@
|
||||
"risk": "low",
|
||||
"title": "获取字段详情",
|
||||
"use_when": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1948,14 +1949,14 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。",
|
||||
"agent_summary": "底层查询/搜索记录,额外支持原子命令的完整分页控制。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"aliases": [
|
||||
"aitable record list"
|
||||
],
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
],
|
||||
"canonical_path": "aitable.query_records",
|
||||
"cli_name": "query",
|
||||
@@ -1977,7 +1978,7 @@
|
||||
"risk": "low",
|
||||
"title": "获取行记录",
|
||||
"use_when": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2009,11 +2010,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "新增记录(cells 的 key 必须是 fieldId)。",
|
||||
"agent_summary": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
],
|
||||
"canonical_path": "aitable.record_create",
|
||||
"cli_name": "create",
|
||||
@@ -2035,7 +2036,7 @@
|
||||
"risk": "medium",
|
||||
"title": "新增记录",
|
||||
"use_when": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2257,11 +2258,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。",
|
||||
"agent_summary": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
],
|
||||
"canonical_path": "aitable.record_update",
|
||||
"cli_name": "update",
|
||||
@@ -2283,7 +2284,7 @@
|
||||
"risk": "medium",
|
||||
"title": "更新记录",
|
||||
"use_when": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2596,7 +2597,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_base_list",
|
||||
"cli_name": "+base-list",
|
||||
@@ -2622,7 +2623,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_base_search",
|
||||
"cli_name": "+base-search",
|
||||
@@ -2752,7 +2753,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_field_get",
|
||||
"cli_name": "+field-get",
|
||||
@@ -2934,7 +2935,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_record_query",
|
||||
"cli_name": "+record-query",
|
||||
@@ -3038,7 +3039,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_resolve_base",
|
||||
"cli_name": "+resolve-base",
|
||||
@@ -3055,7 +3056,7 @@
|
||||
"risk": "low",
|
||||
"title": "按名称搜索多维表 Base 并解析出唯一 baseId(只读)",
|
||||
"use_when": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3064,7 +3065,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_resolve_table",
|
||||
"cli_name": "+resolve-table",
|
||||
@@ -3081,7 +3082,7 @@
|
||||
"risk": "low",
|
||||
"title": "在某个多维表 Base 内按名称解析出唯一的数据表 tableId(只读)",
|
||||
"use_when": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3168,7 +3169,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_table_get",
|
||||
"cli_name": "+table-get",
|
||||
@@ -3382,11 +3383,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "获取数据表结构(字段+视图目录)。",
|
||||
"agent_summary": "底层获取数据表结构、精简字段目录与视图目录。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
],
|
||||
"canonical_path": "aitable.table_get",
|
||||
"cli_name": "get",
|
||||
@@ -3408,7 +3409,7 @@
|
||||
"risk": "low",
|
||||
"title": "获取数据表",
|
||||
"use_when": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -4571,7 +4572,7 @@
|
||||
}
|
||||
],
|
||||
"use_when": [
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -7784,11 +7785,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "给指定消息添加表情回应",
|
||||
"agent_summary": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
],
|
||||
"canonical_path": "chat.add_emoji_reaction",
|
||||
"cli_name": "add-emoji",
|
||||
@@ -7810,7 +7811,7 @@
|
||||
"risk": "medium",
|
||||
"title": "对消息添加 emoji 表情回应",
|
||||
"use_when": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -7967,7 +7968,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
],
|
||||
"canonical_path": "chat.batch_query_group_chat_settings",
|
||||
"cli_name": "query",
|
||||
@@ -7985,7 +7986,7 @@
|
||||
"risk": "low",
|
||||
"title": "批量查询当前用户的群会话设置",
|
||||
"use_when": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -7994,7 +7995,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
],
|
||||
"canonical_path": "chat.batch_update_group_chat_settings",
|
||||
"cli_name": "set",
|
||||
@@ -8012,7 +8013,7 @@
|
||||
"risk": "medium",
|
||||
"title": "批量更新当前用户的群会话设置",
|
||||
"use_when": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -9430,11 +9431,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "查询异步消息发送任务的状态",
|
||||
"agent_summary": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
],
|
||||
"canonical_path": "chat.query_message_send_status",
|
||||
"cli_name": "query-send-status",
|
||||
@@ -9456,7 +9457,7 @@
|
||||
"risk": "low",
|
||||
"title": "查询消息发送状态",
|
||||
"use_when": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -9672,11 +9673,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "移除指定消息上的表情回应",
|
||||
"agent_summary": "从同一条真实消息移除此前添加的同名表情回应",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
],
|
||||
"canonical_path": "chat.remove_emoji_reaction",
|
||||
"cli_name": "remove-emoji",
|
||||
@@ -9698,7 +9699,7 @@
|
||||
"risk": "medium",
|
||||
"title": "移除消息的 emoji 表情回应",
|
||||
"use_when": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -9707,7 +9708,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
],
|
||||
"canonical_path": "chat.remove_group_member",
|
||||
"cli_name": "remove",
|
||||
@@ -9729,7 +9730,7 @@
|
||||
"risk": "medium",
|
||||
"title": "移除群成员",
|
||||
"use_when": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -10342,11 +10343,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "把指定消息设为会话置顶消息",
|
||||
"agent_summary": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
],
|
||||
"canonical_path": "chat.set_pin_message",
|
||||
"cli_name": "set-pin-msg",
|
||||
@@ -10368,7 +10369,7 @@
|
||||
"risk": "medium",
|
||||
"title": "钉住消息(Pin)",
|
||||
"use_when": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -11393,11 +11394,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "查询消息发送状态",
|
||||
"agent_summary": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
],
|
||||
"canonical_path": "chat.shortcut_messages_query_send_status",
|
||||
"cli_name": "+messages-query-send-status",
|
||||
@@ -11414,7 +11415,7 @@
|
||||
"risk": "low",
|
||||
"title": "查询消息发送状态",
|
||||
"use_when": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -11768,11 +11769,11 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "取消指定消息的会话置顶",
|
||||
"agent_summary": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
],
|
||||
"canonical_path": "chat.unset_pin_message",
|
||||
"cli_name": "unset-pin-msg",
|
||||
@@ -11794,7 +11795,7 @@
|
||||
"risk": "medium",
|
||||
"title": "取消钉住消息(Unpin)",
|
||||
"use_when": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -11918,7 +11919,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
],
|
||||
"canonical_path": "chat.update_group_icon",
|
||||
"cli_name": "update-icon",
|
||||
@@ -11940,7 +11941,7 @@
|
||||
"risk": "medium",
|
||||
"title": "更新群头像",
|
||||
"use_when": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -12007,7 +12008,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
],
|
||||
"canonical_path": "chat.update_group_settings",
|
||||
"cli_name": "update-settings",
|
||||
@@ -12029,7 +12030,7 @@
|
||||
"risk": "medium",
|
||||
"title": "更新群设置",
|
||||
"use_when": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -15377,13 +15378,13 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "创建一篇新的在线文档",
|
||||
"agent_summary": "在默认根目录、文档文件夹或知识库根创建带可选初始内容的 adoc",
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"创建表格/脑图/白板/多维表/演示改用 dws wiki node create --type \u003ctype\u003e(勿用 doc create)",
|
||||
"在知识库建空节点实体也可用 wiki node create;本命令侧重可写初始内容的 adoc",
|
||||
"导入本地 Word/Markdown 为在线文档改用 dws doc import(若可用)或 upload --convert"
|
||||
"导入本地 Word/Markdown 为在线文档用 dws doc import,并显式提供 --folder 或 --workspace;不要用已迁移的 doc upload --convert"
|
||||
],
|
||||
"canonical_path": "doc.create_document",
|
||||
"cli_name": "create",
|
||||
@@ -15404,8 +15405,8 @@
|
||||
"risk": "medium",
|
||||
"title": "创建文档",
|
||||
"use_when": [
|
||||
"用户要新建一篇文字在线文档(adoc),可空文档或带初始 Markdown 时",
|
||||
"创建到指定文件夹 --folder、知识库根 --workspace,或默认「我的文档」根目录时"
|
||||
"用户要新建文字在线文档(adoc),可空文档或用 --content-file 写入明确要求的初始 Markdown/JSONML;用户显式要求正文 H1 时必须保留,不能用 --name 替代",
|
||||
"已知目标文档文件夹 --folder 或知识库 --workspace;长 Markdown 仍直接使用本命令,由原生写入管道自动分片;返回 nodeId 绑定同一请求的后续 list/insert/export,后续可观察操作仍须分别执行"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -15536,7 +15537,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"常规文件删除优先 dws drive delete;本入口仅为兼容",
|
||||
"常规文件删除使用 dws drive delete;不要继续选择已弃用的 doc delete 兼容入口",
|
||||
"用户未确认或目标不清时不要删",
|
||||
"删块用 doc block delete;删评论用 doc comment delete"
|
||||
],
|
||||
@@ -15600,7 +15601,8 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"下载钉盘普通文件用 drive download;导出在线文档用 doc export"
|
||||
"下载钉盘普通文件用 drive download;导出在线文档用 doc export",
|
||||
"不要把 blockId/nodeId 当作 resourceId,也不要编造 --output:本命令返回临时 downloadUrl"
|
||||
],
|
||||
"canonical_path": "doc.download_doc_attachment",
|
||||
"cli_name": "download",
|
||||
@@ -15622,7 +15624,7 @@
|
||||
"risk": "low",
|
||||
"title": "下载文档附件",
|
||||
"use_when": [
|
||||
"获取文档正文中附件的临时下载 URL(resourceId 来自 block list attachment)时"
|
||||
"获取文档正文中附件的临时下载 URL 时;resourceId 必须来自本次 block list 返回的 attachment 块"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -15665,7 +15667,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"非 adoc(表格/多维表/普通文件)不要用本命令;先 doc info 再路由",
|
||||
"原始 alidocs URL 类型未知或目标不是 adoc 时先用 drive info 探测并路由;表格/多维表/普通文件不要用本命令",
|
||||
"要元信息用 doc info;要块结构用 doc block list",
|
||||
"Markdown 为有损投影:保形复制模板请用 doc copy,不要 read→create"
|
||||
],
|
||||
@@ -15688,20 +15690,21 @@
|
||||
"risk": "low",
|
||||
"title": "读取文档内容 (Markdown)",
|
||||
"use_when": [
|
||||
"用户要读取钉钉在线文字文档(adoc)正文(Markdown)时",
|
||||
"用户直接粘贴文档 URL 且无其他指令时(默认读内容)",
|
||||
"已由 drive info 确认 extension=adoc,用户要读取正文(Markdown)时",
|
||||
"用户提供已知 adoc nodeId/URL,且要读取内容或抽取指定章节时",
|
||||
"只需标题大纲、指定块区间/单块或特定 JSONML tags 时使用 --content-format jsonml 与 --scope"
|
||||
]
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "获取文档元信息(标题/类型/创建者/权限等)",
|
||||
"agent_summary": "在已确认是 ALIDOC 后读取文档专属元信息",
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"已确认是 adoc 且只要正文改用 dws doc read",
|
||||
"原始 alidocs URL 的类型探测、extension 路由或可靠 fileSize 使用 dws drive info;不要先猜是文档",
|
||||
"已确认是 adoc 且只要正文时改用 dws doc read",
|
||||
"只要目录列表改用 dws drive list / wiki node list",
|
||||
"需要可靠文件大小 fileSize 时改用 dws drive info;文档元信息接口可能不返回大小"
|
||||
"普通文件、电子表格或 AI 表格不使用本命令"
|
||||
],
|
||||
"canonical_path": "doc.get_document_info",
|
||||
"cli_name": "info",
|
||||
@@ -15722,8 +15725,8 @@
|
||||
"risk": "low",
|
||||
"title": "获取文档元信息",
|
||||
"use_when": [
|
||||
"用户要查看文档/节点元信息(标题、类型、创建者、权限)时",
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时"
|
||||
"drive info 已确认是 ALIDOC,用户还要标题、创建者、权限或 docUrl 等文档专属元信息时",
|
||||
"创建响应只有 nodeId、缺少 docUrl,需要补查文档链接时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -15759,7 +15762,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"发起导入用 doc import(若入口可用);不要用本命令代替导入"
|
||||
"发起导入用 doc import;不要先调用 import get,也不要使用示例占位 taskId"
|
||||
],
|
||||
"canonical_path": "doc.import_get",
|
||||
"cli_name": "get",
|
||||
@@ -15780,7 +15783,7 @@
|
||||
"risk": "medium",
|
||||
"title": "查询导入任务结果(手动兜底)",
|
||||
"use_when": [
|
||||
"查询文档导入任务结果(已有 taskId,导入超时/中断后兜底)时"
|
||||
"仅在 doc import 已返回真实 taskId 且自动轮询超时/中断后,查询导入任务结果时"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -15789,8 +15792,9 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"整篇追加 Markdown 优先 doc update --mode append",
|
||||
"插入本地文件附件优先 doc media insert",
|
||||
"仅在用户没有指定块操作、只要整篇追加 Markdown 时优先 doc update --mode append;显式 insert/标题块/列表块不能折叠",
|
||||
"插入本地文件附件优先 doc media insert;不要为普通附件手写 attachment JSON",
|
||||
"整段 Markdown 或长内容优先 doc update --mode append,不要拆成大量 block insert",
|
||||
"删块用 block delete;改已有块用 block update"
|
||||
],
|
||||
"canonical_path": "doc.insert_document_block",
|
||||
@@ -15813,7 +15817,9 @@
|
||||
"risk": "medium",
|
||||
"title": "插入块元素",
|
||||
"use_when": [
|
||||
"在文档中插入新块(段落/标题等);简单场景用 --text/--heading,复杂块用 --element JSON"
|
||||
"用户明确要求在文档中插入新块(段落/标题/列表等)时;简单段落/标题用 --text/--heading,复杂块用 --element JSON",
|
||||
"用户要求有序列表块时使用 JSONML p.list.isOrdered=true(同一 listId)或等价原生 orderedList 结构,不能用普通 Markdown 代替",
|
||||
"相对插入时先 block list 获取真实 blockId,再同时传 --ref-block 与 --where before|after"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -15876,7 +15882,7 @@
|
||||
"risk": "low",
|
||||
"title": "查询块元素",
|
||||
"use_when": [
|
||||
"查看文档一级块结构、拿 blockId,供 insert/update/delete 或划词评论定位时"
|
||||
"查看文档一级块结构、拿 blockId,供 insert/update/delete 或划词评论定位时;用户在显式工作流中点名 list 时必须真实执行并使用返回结果"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -16105,7 +16111,8 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"新建评论用 create/create-inline;删评论用 delete"
|
||||
"新建评论用 create/create-inline;删评论用 delete",
|
||||
"不要把 commentId、blockId 或示例占位符传给 --comment-key"
|
||||
],
|
||||
"canonical_path": "doc.reply_comment",
|
||||
"cli_name": "reply",
|
||||
@@ -16127,7 +16134,7 @@
|
||||
"risk": "medium",
|
||||
"title": "回复评论",
|
||||
"use_when": [
|
||||
"回复已有评论(文字、可 @用户/@群,或 --emoji 表情);commentKey 来自 list/create"
|
||||
"回复已有评论(文字、可 @用户/@群,或 --emoji 表情);commentKey 必须来自本次 list/create 返回"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -16244,7 +16251,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"常规文件管理优先 drive copy;要搬走原件用 move;复制后必须从真实返回取副本 nodeId,禁止继续编辑源文档"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_copy",
|
||||
"cli_name": "+copy",
|
||||
@@ -16270,7 +16277,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"长、多行、表格或文件内容使用 doc update --mode append --content-file;指定位置或富结构使用 block insert;不要跳过 user_required 确认"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_doc_append",
|
||||
"cli_name": "+doc-append",
|
||||
@@ -16322,7 +16329,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"用户要直接拿到本地 docx/markdown/pdf 文件时使用一体化 dws doc export;不要默认手工编排 submit/get 轮询"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_export_submit",
|
||||
"cli_name": "+export-submit",
|
||||
@@ -16348,7 +16355,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要最近访问、extension/时间/创建者等组合过滤时使用 +search;目标已给 nodeId/URL 时直接进入 info/read,不再搜索"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_find_doc",
|
||||
"cli_name": "+find-doc",
|
||||
@@ -16374,7 +16381,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"全局或钉盘目录浏览优先 drive list,知识库节点树优先 wiki node list;本 Shortcut 只列已知 doc folder/workspace 的直接子节点"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_list",
|
||||
"cli_name": "+list",
|
||||
@@ -16400,7 +16407,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"常规文件管理优先 drive move;要保留原位置副本用 copy;目标位置不明确或未确认时不要移动"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_move",
|
||||
"cli_name": "+move",
|
||||
@@ -16426,7 +16433,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只有一个关键词且只要紧凑标题/URL/type/token 投影时优先 +find-doc;目标已给 nodeId/URL 时不要再搜索"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_search",
|
||||
"cli_name": "+search",
|
||||
@@ -16452,7 +16459,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"同名人员未消歧、缺少真实文档 URL 或用户未确认时不要发送;本命令不授予文档权限,授权应走 drive permission"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_share_doc",
|
||||
"cli_name": "+share-doc",
|
||||
@@ -16556,7 +16563,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只查看历史用 +version-list,只保存当前快照用 +version-save;版本号未核实、用户未确认或只需改单块时不要回滚"
|
||||
],
|
||||
"canonical_path": "doc.shortcut_version_revert",
|
||||
"cli_name": "+version-revert",
|
||||
@@ -16820,12 +16827,13 @@
|
||||
},
|
||||
{
|
||||
"agent_metadata_source": "embedded-skill-metadata",
|
||||
"agent_summary": "更新文档内容(追加 / 覆盖;覆盖需 --yes)",
|
||||
"agent_summary": "用原生自动分片管道追加或整篇覆盖 adoc 内容",
|
||||
"agent_summary_source": "dws-agent-selection/doc",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"目标不是 adoc 或只要改单个块时改用 doc block update",
|
||||
"覆盖模式用户未确认前不要执行;可先 --dry-run 预览",
|
||||
"覆盖模式用户未确认前不要执行;先 --dry-run 预览,确认后再加 --yes",
|
||||
"--content 与 --content-file 二选一;--index 仅用于 mode=append",
|
||||
"创建新文档用 doc create,不要用 update 冒充创建"
|
||||
],
|
||||
"canonical_path": "doc.update_document",
|
||||
@@ -16847,9 +16855,10 @@
|
||||
"risk": "medium",
|
||||
"title": "更新文档内容",
|
||||
"use_when": [
|
||||
"用户要向已有 adoc 追加内容时用 --mode append(更安全)",
|
||||
"用户要向已有 adoc 追加长、多行或文件内容时用 --mode append + --content-file;CLI 自动分片",
|
||||
"用户明确要求整篇覆盖替换时用 --mode overwrite(破坏性)",
|
||||
"append 且要插到第 N 个 block 前时加 --index N(先 block list)"
|
||||
"append 且要插到第 N 个 block 前时加 --index N(先 block list)",
|
||||
"长文本、多行内容或表格优先写入 UTF-8 文件并使用 --content-file,避免 shell 转义损坏"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -16859,7 +16868,8 @@
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"插入新块用 block insert;删除用 block delete",
|
||||
"改文档显示名用 rename;整篇覆盖用 update overwrite"
|
||||
"改文档显示名用 rename;整篇覆盖用 update overwrite",
|
||||
"不要跨文档复用 blockId,也不要把 nodeId/commentKey 当作 blockId"
|
||||
],
|
||||
"canonical_path": "doc.update_document_block",
|
||||
"cli_name": "update",
|
||||
@@ -16881,7 +16891,7 @@
|
||||
"risk": "medium",
|
||||
"title": "更新块元素",
|
||||
"use_when": [
|
||||
"修改已有块的文本/标题/样式(已知 blockId)时"
|
||||
"修改已有块的文本/标题/样式时;blockId 必须来自目标文档本次 block list 结果"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -18283,8 +18293,10 @@
|
||||
"agent_summary_source": "dws-agent-selection/drive",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
],
|
||||
@@ -18307,7 +18319,8 @@
|
||||
"title": "上传本地文件到钉盘或文档空间",
|
||||
"use_when": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8704,13 +8704,15 @@
|
||||
"internal/cli/schema_hints/metadata/aitable.json",
|
||||
"internal/cli/schema_hints/selection/aitable.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.base_search",
|
||||
"skills/mono/references/products/aitable.md"
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md"
|
||||
],
|
||||
"agent_summary": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。",
|
||||
"agent_summary": "底层按名称搜索 AI 表格 Base,返回原始候选列表。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
],
|
||||
"canonical_path": "aitable.base_search",
|
||||
"cli_name": "search",
|
||||
@@ -8731,14 +8733,14 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。"
|
||||
"value": "底层按名称搜索 AI 表格 Base,返回原始候选列表。"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。"
|
||||
"value": "底层按名称搜索 AI 表格 Base,返回原始候选列表。"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -8770,7 +8772,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -8779,7 +8781,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -9009,7 +9011,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -9018,7 +9020,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -9237,7 +9239,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "搜索 AI 表格",
|
||||
"use_when": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
]
|
||||
},
|
||||
"aitable.base_update": {
|
||||
@@ -23545,13 +23547,15 @@
|
||||
"internal/cli/schema_hints/selection/aitable.json",
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.field_get",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-field.md"
|
||||
"skills/mono/references/products/aitable/aitable-field.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-field.md"
|
||||
],
|
||||
"agent_summary": "获取字段完整配置。",
|
||||
"agent_summary": "底层获取字段完整类型与 config。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
],
|
||||
"canonical_path": "aitable.field_get",
|
||||
"cli_name": "get",
|
||||
@@ -23572,14 +23576,14 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "获取字段完整配置。"
|
||||
"value": "底层获取字段完整类型与 config。"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "获取字段完整配置。"
|
||||
"value": "底层获取字段完整类型与 config。"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -23611,7 +23615,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -23620,7 +23624,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -23848,7 +23852,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -23857,7 +23861,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -24201,7 +24205,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "获取字段详情",
|
||||
"use_when": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
"aitable.field_list": {
|
||||
@@ -38119,16 +38123,18 @@
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.query_records",
|
||||
"skills/mono/references/products/aitable-record-ops.md",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-record-query.md"
|
||||
"skills/mono/references/products/aitable/aitable-record-query.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-query.md"
|
||||
],
|
||||
"agent_summary": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。",
|
||||
"agent_summary": "底层查询/搜索记录,额外支持原子命令的完整分页控制。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"aliases": [
|
||||
"aitable record list"
|
||||
],
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
],
|
||||
"canonical_path": "aitable.query_records",
|
||||
"cli_name": "query",
|
||||
@@ -38149,14 +38155,14 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。"
|
||||
"value": "底层查询/搜索记录,额外支持原子命令的完整分页控制。"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。"
|
||||
"value": "底层查询/搜索记录,额外支持原子命令的完整分页控制。"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -38188,7 +38194,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -38197,7 +38203,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -38447,7 +38453,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -38456,7 +38462,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -39699,7 +39705,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "获取行记录",
|
||||
"use_when": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
"aitable.record_batch_update": {
|
||||
@@ -40397,13 +40403,15 @@
|
||||
"skills/mono/references/products/aitable-record-ops.md",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-attachment.md",
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md"
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-create.md"
|
||||
],
|
||||
"agent_summary": "新增记录(cells 的 key 必须是 fieldId)。",
|
||||
"agent_summary": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
],
|
||||
"canonical_path": "aitable.record_create",
|
||||
"cli_name": "create",
|
||||
@@ -40424,14 +40432,14 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "新增记录(cells 的 key 必须是 fieldId)。"
|
||||
"value": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "新增记录(cells 的 key 必须是 fieldId)。"
|
||||
"value": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -40463,7 +40471,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -40472,7 +40480,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -40700,7 +40708,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -40709,7 +40717,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -41156,7 +41164,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "新增记录",
|
||||
"use_when": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
]
|
||||
},
|
||||
"aitable.record_delete": {
|
||||
@@ -46549,13 +46557,15 @@
|
||||
"internal/cli/schema_mcp_metadata.json#tools.aitable.record_update",
|
||||
"skills/mono/references/products/aitable-record-ops.md",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-attachment.md"
|
||||
"skills/mono/references/products/aitable/aitable-attachment.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-update.md"
|
||||
],
|
||||
"agent_summary": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。",
|
||||
"agent_summary": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
],
|
||||
"canonical_path": "aitable.record_update",
|
||||
"cli_name": "update",
|
||||
@@ -46576,14 +46586,14 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。"
|
||||
"value": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。"
|
||||
"value": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -46615,7 +46625,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -46624,7 +46634,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -46852,7 +46862,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -46861,7 +46871,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -47308,7 +47318,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "更新记录",
|
||||
"use_when": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
]
|
||||
},
|
||||
"aitable.record_upsert": {
|
||||
@@ -53447,7 +53457,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_base_list",
|
||||
"cli_name": "+base-list",
|
||||
@@ -53502,7 +53512,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -53511,7 +53521,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/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -53943,7 +53953,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_base_search",
|
||||
"cli_name": "+base-search",
|
||||
@@ -53997,7 +54007,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -54006,7 +54016,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/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -56196,7 +56206,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_field_get",
|
||||
"cli_name": "+field-get",
|
||||
@@ -56250,7 +56260,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -56259,7 +56269,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/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -60316,7 +60326,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_record_query",
|
||||
"cli_name": "+record-query",
|
||||
@@ -60370,7 +60380,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -60379,7 +60389,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/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -63485,7 +63495,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_resolve_base",
|
||||
"cli_name": "+resolve-base",
|
||||
@@ -63539,7 +63549,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -63548,7 +63558,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/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -63758,7 +63768,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -63767,7 +63777,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/aitable.json",
|
||||
"value": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -63884,7 +63894,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "按名称搜索多维表 Base 并解析出唯一 baseId(只读)",
|
||||
"use_when": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
]
|
||||
},
|
||||
"aitable.shortcut_resolve_table": {
|
||||
@@ -63900,7 +63910,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_resolve_table",
|
||||
"cli_name": "+resolve-table",
|
||||
@@ -63954,7 +63964,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -63963,7 +63973,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/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -64173,7 +64183,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -64182,7 +64192,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/aitable.json",
|
||||
"value": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -64395,7 +64405,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "在某个多维表 Base 内按名称解析出唯一的数据表 tableId(只读)",
|
||||
"use_when": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
]
|
||||
},
|
||||
"aitable.shortcut_role_list": {
|
||||
@@ -65656,7 +65666,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
],
|
||||
"canonical_path": "aitable.shortcut_table_get",
|
||||
"cli_name": "+table-get",
|
||||
@@ -65711,7 +65721,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -65720,7 +65730,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/aitable.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -70522,13 +70532,15 @@
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/mono/references/products/aitable/aitable-advperm.md",
|
||||
"skills/mono/references/products/aitable/aitable-primary-doc.md",
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md"
|
||||
"skills/mono/references/products/aitable/aitable-record-create.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md"
|
||||
],
|
||||
"agent_summary": "获取数据表结构(字段+视图目录)。",
|
||||
"agent_summary": "底层获取数据表结构、精简字段目录与视图目录。",
|
||||
"agent_summary_source": "dws-agent-selection/aitable",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
],
|
||||
"canonical_path": "aitable.table_get",
|
||||
"cli_name": "get",
|
||||
@@ -70549,14 +70561,14 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "获取数据表结构(字段+视图目录)。"
|
||||
"value": "底层获取数据表结构、精简字段目录与视图目录。"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": "获取数据表结构(字段+视图目录)。"
|
||||
"value": "底层获取数据表结构、精简字段目录与视图目录。"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -70588,7 +70600,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -70597,7 +70609,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -70825,7 +70837,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -70834,7 +70846,7 @@
|
||||
"review_reason": "人工结合实时 dws schema MCP 描述(或 helper 无 live 时用 Skill/Cobra)、兄弟命令分流与 Runtime 确认门禁审阅选型语义。",
|
||||
"source": "internal/cli/schema_hints/selection/aitable.json",
|
||||
"value": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -71081,7 +71093,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "获取数据表",
|
||||
"use_when": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
]
|
||||
},
|
||||
"aitable.table_list": {
|
||||
|
||||
@@ -1158,11 +1158,11 @@
|
||||
"skills/mono/SKILL.md",
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"agent_summary": "给指定消息添加表情回应",
|
||||
"agent_summary": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
],
|
||||
"canonical_path": "chat.add_emoji_reaction",
|
||||
"cli_name": "add-emoji",
|
||||
@@ -1193,14 +1193,14 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "给指定消息添加表情回应"
|
||||
"value": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "给指定消息添加表情回应"
|
||||
"value": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -1232,7 +1232,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -1241,7 +1241,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -1479,7 +1479,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -1488,7 +1488,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -2220,7 +2220,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "对消息添加 emoji 表情回应",
|
||||
"use_when": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
]
|
||||
},
|
||||
"chat.add_group_member": {
|
||||
@@ -6372,7 +6372,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
],
|
||||
"canonical_path": "chat.batch_query_group_chat_settings",
|
||||
"cli_name": "query",
|
||||
@@ -6426,7 +6426,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -6435,7 +6435,7 @@
|
||||
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -6645,7 +6645,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -6654,7 +6654,7 @@
|
||||
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -6773,7 +6773,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "批量查询当前用户的群会话设置",
|
||||
"use_when": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
]
|
||||
},
|
||||
"chat.batch_update_group_chat_settings": {
|
||||
@@ -6789,7 +6789,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
],
|
||||
"canonical_path": "chat.batch_update_group_chat_settings",
|
||||
"cli_name": "set",
|
||||
@@ -6843,7 +6843,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -6852,7 +6852,7 @@
|
||||
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -7062,7 +7062,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -7071,7 +7071,7 @@
|
||||
"review_reason": "人工审阅:对照悟空 develop 同名命令实现、Cobra Long/Example 与 Runtime 确认门禁移植;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -7190,7 +7190,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "批量更新当前用户的群会话设置",
|
||||
"use_when": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
]
|
||||
},
|
||||
"chat.clear_all_red_point": {
|
||||
@@ -38313,11 +38313,11 @@
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"agent_summary": "查询异步消息发送任务的状态",
|
||||
"agent_summary": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
],
|
||||
"canonical_path": "chat.query_message_send_status",
|
||||
"cli_name": "query-send-status",
|
||||
@@ -38338,14 +38338,14 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "查询异步消息发送任务的状态"
|
||||
"value": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "查询异步消息发送任务的状态"
|
||||
"value": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -38377,7 +38377,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -38386,7 +38386,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -38608,7 +38608,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -38617,7 +38617,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -38767,7 +38767,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "查询消息发送状态",
|
||||
"use_when": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
]
|
||||
},
|
||||
"chat.query_msg_read_status": {
|
||||
@@ -43352,11 +43352,11 @@
|
||||
"skills/mono/SKILL.md",
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"agent_summary": "移除指定消息上的表情回应",
|
||||
"agent_summary": "从同一条真实消息移除此前添加的同名表情回应",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
],
|
||||
"canonical_path": "chat.remove_emoji_reaction",
|
||||
"cli_name": "remove-emoji",
|
||||
@@ -43387,14 +43387,14 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "移除指定消息上的表情回应"
|
||||
"value": "从同一条真实消息移除此前添加的同名表情回应"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "移除指定消息上的表情回应"
|
||||
"value": "从同一条真实消息移除此前添加的同名表情回应"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -43426,7 +43426,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -43435,7 +43435,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -43673,7 +43673,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -43682,7 +43682,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -44414,7 +44414,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "移除消息的 emoji 表情回应",
|
||||
"use_when": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
]
|
||||
},
|
||||
"chat.remove_group_member": {
|
||||
@@ -44435,7 +44435,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
],
|
||||
"canonical_path": "chat.remove_group_member",
|
||||
"cli_name": "remove",
|
||||
@@ -44495,7 +44495,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -44504,7 +44504,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -44759,7 +44759,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -44768,7 +44768,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -45075,7 +45075,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "移除群成员",
|
||||
"use_when": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
]
|
||||
},
|
||||
"chat.remove_message_favorite": {
|
||||
@@ -65014,11 +65014,11 @@
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"agent_summary": "把指定消息设为会话置顶消息",
|
||||
"agent_summary": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
],
|
||||
"canonical_path": "chat.set_pin_message",
|
||||
"cli_name": "set-pin-msg",
|
||||
@@ -65039,14 +65039,14 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "把指定消息设为会话置顶消息"
|
||||
"value": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "把指定消息设为会话置顶消息"
|
||||
"value": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -65078,7 +65078,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -65087,7 +65087,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -65309,7 +65309,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -65318,7 +65318,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -65598,7 +65598,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "钉住消息(Pin)",
|
||||
"use_when": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
]
|
||||
},
|
||||
"chat.set_top_conversation": {
|
||||
@@ -87865,11 +87865,11 @@
|
||||
"internal/cli/schema_hints/metadata/chat.json",
|
||||
"internal/cli/schema_hints/selection/chat.json"
|
||||
],
|
||||
"agent_summary": "查询消息发送状态",
|
||||
"agent_summary": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
],
|
||||
"canonical_path": "chat.shortcut_messages_query_send_status",
|
||||
"cli_name": "+messages-query-send-status",
|
||||
@@ -87890,14 +87890,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/chat.json",
|
||||
"value": "查询消息发送状态"
|
||||
"value": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功"
|
||||
}
|
||||
],
|
||||
"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/chat.json",
|
||||
"value": "查询消息发送状态"
|
||||
"value": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -87923,7 +87923,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -87932,7 +87932,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/chat.json",
|
||||
"value": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -88142,7 +88142,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -88151,7 +88151,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/chat.json",
|
||||
"value": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -88268,7 +88268,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "查询消息发送状态",
|
||||
"use_when": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
]
|
||||
},
|
||||
"chat.shortcut_messages_read_status": {
|
||||
@@ -101127,11 +101127,11 @@
|
||||
"skills/mono/references/intent-guide.md",
|
||||
"skills/mono/references/products/chat.md"
|
||||
],
|
||||
"agent_summary": "取消指定消息的会话置顶",
|
||||
"agent_summary": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin",
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
],
|
||||
"canonical_path": "chat.unset_pin_message",
|
||||
"cli_name": "unset-pin-msg",
|
||||
@@ -101152,14 +101152,14 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "取消指定消息的会话置顶"
|
||||
"value": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin"
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": "取消指定消息的会话置顶"
|
||||
"value": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin"
|
||||
},
|
||||
"availability": {
|
||||
"candidates": [
|
||||
@@ -101191,7 +101191,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -101200,7 +101200,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -101422,7 +101422,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -101431,7 +101431,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -101711,7 +101711,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "取消钉住消息(Unpin)",
|
||||
"use_when": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
]
|
||||
},
|
||||
"chat.unset_top_message": {
|
||||
@@ -104378,7 +104378,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
],
|
||||
"canonical_path": "chat.update_group_icon",
|
||||
"cli_name": "update-icon",
|
||||
@@ -104438,7 +104438,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -104447,7 +104447,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -104669,7 +104669,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -104678,7 +104678,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -105000,7 +105000,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "更新群头像",
|
||||
"use_when": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
]
|
||||
},
|
||||
"chat.update_group_name": {
|
||||
@@ -106217,7 +106217,7 @@
|
||||
"agent_summary_source": "dws-agent-selection/chat",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
],
|
||||
"canonical_path": "chat.update_group_settings",
|
||||
"cli_name": "update-settings",
|
||||
@@ -106277,7 +106277,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -106286,7 +106286,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
]
|
||||
},
|
||||
"canonical_path": {
|
||||
@@ -106508,7 +106508,7 @@
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -106517,7 +106517,7 @@
|
||||
"review_reason": "AI 生成并复核的 Agent 选择语义,依据精确 CommandRegistry identity、真实 Cobra help 与产品参考,不改写执行、安全或接口事实。",
|
||||
"source": "internal/cli/schema_hints/selection/chat.json",
|
||||
"value": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -106921,7 +106921,7 @@
|
||||
"source": "reviewed_command_registry",
|
||||
"title": "更新群设置",
|
||||
"use_when": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
]
|
||||
},
|
||||
"chat.update_notification_off": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27242,8 +27242,10 @@
|
||||
"agent_summary_source": "dws-agent-selection/drive",
|
||||
"availability": "available",
|
||||
"avoid_when": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
],
|
||||
@@ -27267,7 +27269,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": "上传本地文件到钉盘或文档空间,或按节点 ID 确认覆盖已有文件"
|
||||
@@ -27275,7 +27277,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": "上传本地文件到钉盘或文档空间,或按节点 ID 确认覆盖已有文件"
|
||||
},
|
||||
@@ -27299,12 +27301,14 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
]
|
||||
@@ -27312,11 +27316,13 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
]
|
||||
@@ -27429,7 +27435,7 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
@@ -27440,7 +27446,7 @@
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"dws drive upload --file ./report.pdf --format json",
|
||||
@@ -27528,7 +27534,7 @@
|
||||
},
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"selected": false,
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": true
|
||||
@@ -27580,23 +27586,25 @@
|
||||
"candidates": [
|
||||
{
|
||||
"precedence": "reviewed_explicit",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"selected": true,
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
]
|
||||
}
|
||||
],
|
||||
"precedence": "reviewed_explicit",
|
||||
"resolution": "highest_precedence",
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"source": "internal/cli/schema_hints/selection/drive.json",
|
||||
"value": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
]
|
||||
}
|
||||
@@ -28319,7 +28327,8 @@
|
||||
"title": "上传本地文件到钉盘或文档空间",
|
||||
"use_when": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -289,12 +289,12 @@
|
||||
]
|
||||
},
|
||||
"aitable.base_search": {
|
||||
"agent_summary": "按名称搜索 AI 表格 Base(优先于仅最近访问的 list)。",
|
||||
"agent_summary": "底层按名称搜索 AI 表格 Base,返回原始候选列表。",
|
||||
"use_when": [
|
||||
"用户要找某个 AI 表格/多维表,按名称检索时优先使用"
|
||||
"需要 Shortcut 未公开的底层参数、原始 search_bases 响应或自定义候选处理时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"只要最近访问列表用 base list;已知 baseId 取详情用 base get;电子表格 axls 用 sheet"
|
||||
"普通按名称解析唯一 baseId 优先用 +resolve-base;只要候选列表用 +base-search;最近访问用 +base-list;电子表格 axls 用 sheet"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable base search --query \"项目\""
|
||||
@@ -304,7 +304,8 @@
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"cobra-help:dws aitable base search --help",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md",
|
||||
"dws-schema-live:aitable.search_bases"
|
||||
]
|
||||
},
|
||||
@@ -710,12 +711,12 @@
|
||||
]
|
||||
},
|
||||
"aitable.field_get": {
|
||||
"agent_summary": "获取字段完整配置。",
|
||||
"agent_summary": "底层获取字段完整类型与 config。",
|
||||
"use_when": [
|
||||
"需要字段类型/config(选项、公式等)详情时"
|
||||
"需要 +field-get 未公开的底层参数、原始响应或不同执行语义时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"表级字段目录也可用 table get;创建字段用 field create;不可用本命令改类型"
|
||||
"正常按字段 ID 展开类型/config 优先用 +field-get;精简字段目录用 +table-get;创建字段用 field create"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable field get --base-id <BASE_ID> --table-id <TABLE_ID>"
|
||||
@@ -725,7 +726,8 @@
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"cobra-help:dws aitable field get --help",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-field.md",
|
||||
"dws-schema-live:aitable.get_fields"
|
||||
]
|
||||
},
|
||||
@@ -1110,12 +1112,12 @@
|
||||
]
|
||||
},
|
||||
"aitable.query_records": {
|
||||
"agent_summary": "查询/搜索记录(filters/sort/分页/--all;cells 键为 fieldId)。",
|
||||
"agent_summary": "底层查询/搜索记录,额外支持原子命令的完整分页控制。",
|
||||
"use_when": [
|
||||
"查看、筛选、全文搜索或遍历记录时的主入口"
|
||||
"需要 +record-query 未公开的 --all/--page-limit、底层原始响应或不同执行语义时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"已知 recordId 窄查可用 record get;空行用 query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
"普通按 ID、关键词、filters、sort 或 cursor 查询优先用 +record-query;空行用 +record-query-empty;写入用 create/update;电子表格单元格用 sheet"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable record query --base-id <BASE_ID> --table-id <TABLE_ID>"
|
||||
@@ -1125,7 +1127,8 @@
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"cobra-help:dws aitable record query --help",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-query.md",
|
||||
"dws-schema-live:aitable.query_records"
|
||||
]
|
||||
},
|
||||
@@ -1150,12 +1153,12 @@
|
||||
]
|
||||
},
|
||||
"aitable.record_create": {
|
||||
"agent_summary": "新增记录(cells 的 key 必须是 fieldId)。",
|
||||
"agent_summary": "新增记录;使用 fieldId 写入并从 newRecordIds 回读验证。",
|
||||
"use_when": [
|
||||
"需要插入新行数据时"
|
||||
"已取得 baseId/tableId 与字段完整配置,需要插入一条或多条新记录并回读时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"更新已有行用 record update;有则更无则增用 upsert;批量同 patch 用 batch-update"
|
||||
"未核对字段类型时先 field get;更新已有行用 record update;有则更无则增用 upsert;本地 CSV/JSON 批量追加可用已评审脚本"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable record create --base-id <BASE_ID> --table-id <TABLE_ID> --records '[{\"cells\":{\"fldXXX\":\"值\"}}]'"
|
||||
@@ -1165,7 +1168,8 @@
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"cobra-help:dws aitable record create --help",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-create.md",
|
||||
"dws-schema-live:aitable.create_records"
|
||||
]
|
||||
},
|
||||
@@ -1310,12 +1314,12 @@
|
||||
]
|
||||
},
|
||||
"aitable.record_update": {
|
||||
"agent_summary": "更新已有记录字段(先 query 拿 recordId;只传需改字段)。",
|
||||
"agent_summary": "更新已有记录字段;先 query 拿 recordId,使用 fieldId 写入并回读。",
|
||||
"use_when": [
|
||||
"需要修改已有记录若干字段时"
|
||||
"已查询得到真实 recordId 并核对字段类型,需要修改每条记录各自的 cells 时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
"未定位 recordId 或字段配置时先 query/field get;新建用 create;多条共用同一 cells patch 用 batch-update;混合创建/更新用 upsert"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable record update --base-id <BASE_ID> --table-id <TABLE_ID> --records '[{\"recordId\":\"recXXX\",\"cells\":{\"fldYYY\":\"新值\"}}]'"
|
||||
@@ -1325,7 +1329,8 @@
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"cobra-help:dws aitable record update --help",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable/aitable-record-update.md",
|
||||
"dws-schema-live:aitable.update_records"
|
||||
]
|
||||
},
|
||||
@@ -1550,12 +1555,12 @@
|
||||
]
|
||||
},
|
||||
"aitable.table_get": {
|
||||
"agent_summary": "获取数据表结构(字段+视图目录)。",
|
||||
"agent_summary": "底层获取数据表结构、精简字段目录与视图目录。",
|
||||
"use_when": [
|
||||
"需要字段 fieldId 或视图目录以继续 record/field 操作时优先 table get"
|
||||
"需要 +table-get 未公开的底层参数、原始 get_tables 响应或不同执行语义时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"只关心 Base 级 tables 列表可先 base get;字段完整配置也可用 field get"
|
||||
"正常获取表、精简字段和视图目录优先用 +table-get;按表名解析 ID 用 +resolve-table;完整字段 config 用 +field-get"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable table get --base-id BASE_ID"
|
||||
@@ -1565,7 +1570,8 @@
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"cobra-help:dws aitable table get --help",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md",
|
||||
"dws-schema-live:aitable.get_tables"
|
||||
]
|
||||
},
|
||||
@@ -2335,7 +2341,7 @@
|
||||
"当你不知道具体 baseId、想先浏览自己最近用过或可访问的 AI 表格清单以便定位目标时使用;支持游标分页,返回 Base 列表及其 baseId。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"知道名称并要解析唯一 baseId 用 +resolve-base;按关键词要完整候选用 +base-search;不要把最近访问列表描述成全部 Base"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable +base-list",
|
||||
@@ -2355,7 +2361,7 @@
|
||||
"当你知道某个 AI 表格的名字或部分关键词、想直接定位到它并拿到 baseId 时使用;输入名称关键词,返回匹配的 Base 列表。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需唯一 baseId 用 +resolve-base;浏览最近访问用 +base-list;需要未公开底层参数或原始响应才用 base search"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable +base-search --query \"项目管理\""
|
||||
@@ -2393,7 +2399,7 @@
|
||||
"当你已进入某个 Base、需要了解其中某些数据表有哪些字段(拿 fieldId)、有哪些视图(拿 viewId)以便读写数据时使用;批量返回表信息、字段目录和视图目录。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只知道表名并要解析唯一 tableId 用 +resolve-table;需要字段完整 config 用 +field-get;需要未公开底层语义才用 table get"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable +table-get --base-id BASE_ID",
|
||||
@@ -2413,7 +2419,7 @@
|
||||
"当你需要查看字段的完整类型配置(如单选选项、关联表设置、AI 配置)以便正确写入数据或改配置时使用;批量返回字段详情。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只需精简 fieldId/type 目录用 +table-get;创建或修改字段用 field create/update;需要未公开底层语义才用 field get"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable +field-get --base-id B --table-id T"
|
||||
@@ -2432,7 +2438,7 @@
|
||||
"当你要读取表格里的行数据——按 recordId 精确取、按结构化条件筛选、按关键词全文搜索或分页遍历时使用;返回匹配记录及其单元格值。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"完全空行扫描用 +record-query-empty;变更历史用 +record-history-list;需要 --all/--page-limit 或未公开底层语义才用 record query"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable +record-query --base-id B --table-id T --query \"关键词\" --limit 50"
|
||||
@@ -2849,10 +2855,10 @@
|
||||
"aitable.shortcut_resolve_base": {
|
||||
"agent_summary": "按名称搜索多维表 Base 并解析出唯一 baseId(只读)",
|
||||
"use_when": [
|
||||
"当你只知道某个多维表 Base 的名称(或名称里的关键词)、想把它解析成可直接用于后续工具的 baseId 时使用;内部按 --name 关键词调用 search_bases 搜索 Base,再在本地投影出每个候选的 baseId 和 name。如果只命中一个 Base 就直接返回它的 baseId;如果命中多个则列出全部候选让你消歧,绝不替你瞎猜;如果一个都没命中则提示未找到。这是纯只读操作,只做搜索与本地投影,不会修改任何 Base。"
|
||||
"只知道 Base 名称或关键词,需要解析成唯一 baseId 供后续 Table/Record 操作时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;只要完整候选列表用 +base-search;浏览最近访问用 +base-list;已知 baseId 不再重复搜索"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable +resolve-base --name 项目管理"
|
||||
@@ -2868,10 +2874,10 @@
|
||||
"aitable.shortcut_resolve_table": {
|
||||
"agent_summary": "在某个多维表 Base 内按名称解析出唯一的数据表 tableId(只读)",
|
||||
"use_when": [
|
||||
"当你已经知道某个多维表 Base 的 baseId、又只记得里面某张数据表(table)的名称或名称关键词、想把它解析成可直接用于后续工具的 tableId 时使用;内部先用 get_tables(只传 baseId)列出该 Base 下的全部数据表,再在本地把每张表投影成 tableId、name,并按 --name 关键词做大小写不敏感的包含匹配来筛选候选。如果只命中一张表就直接返回它的 tableId;如果命中多张则列出全部候选让你消歧,绝不替你瞎猜;如果一张都没命中则提示未找到。这是纯只读操作,只做列举、本地匹配与投影,不会创建、修改或删除任何数据表。"
|
||||
"已有真实 baseId、只知道数据表名称或关键词,需要解析成唯一 tableId 时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"多候选时必须让用户消歧;需要所有表及字段/视图目录用 +table-get;已知 tableId 不再重复解析"
|
||||
],
|
||||
"examples": [
|
||||
"dws aitable +resolve-table --base B --name 任务"
|
||||
@@ -2887,18 +2893,19 @@
|
||||
},
|
||||
"products": {
|
||||
"aitable": {
|
||||
"agent_summary": "管理 AI 表格 Base、数据表、字段、记录、视图、表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"agent_summary": "管理 AI 表格的 Base、Table、Field、Record、视图表单、仪表盘、权限、导入导出与自动化工作流。",
|
||||
"use_when": [
|
||||
"需要读取或管理 AI 表格中的结构、数据、视图、权限、导入导出或工作流时"
|
||||
"目标是 AI 表格/多维表中的结构化 Base、数据表、字段、记录、视图、权限、文件导入导出或自动化工作流时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"目标是在线电子表格单元格读写时用 sheet;普通文档用 doc"
|
||||
"在线电子表格工作表、单元格或公式用 sheet;普通文档用 doc;钉盘普通文件用 drive;类型不明的 alidocs URL 先做类型预检"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工结合实时 MCP/Skill/Cobra 审阅产品级选择边界。",
|
||||
"review_reason": "人工结合多产品边界、根 Skill 的渐进加载顺序、真实 Cobra/Shortcut 路径与 MCP 语义审阅产品级选择边界。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json",
|
||||
"skills/mono/references/products/aitable.md",
|
||||
"skills/multi/dingtalk-aitable/SKILL.md",
|
||||
"skills/multi/dingtalk-aitable/references/aitable.md",
|
||||
"dws-schema-live:product-index"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@
|
||||
]
|
||||
},
|
||||
"chat.add_emoji_reaction": {
|
||||
"agent_summary": "给指定消息添加表情回应",
|
||||
"agent_summary": "使用同一会话中真实的 openMessageId 给指定消息添加表情回应",
|
||||
"use_when": [
|
||||
"需要对已有消息添加一个 emoji reaction 时"
|
||||
"需要对已有消息添加一个 emoji reaction 时;conversation-id 与 msg-id 必须来自同一条真实消息"
|
||||
],
|
||||
"avoid_when": [
|
||||
"发送文本消息或文字表情时不要使用"
|
||||
"发送文本消息或文字表情时不要使用;不要把 openTaskId 当作 msg-id"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat message add-emoji --conversation-id <openConversationId> --msg-id <openMessageId> --emoji \"赞\""
|
||||
@@ -658,12 +658,12 @@
|
||||
]
|
||||
},
|
||||
"chat.query_message_send_status": {
|
||||
"agent_summary": "查询异步消息发送任务的状态",
|
||||
"agent_summary": "查询异步消息发送任务的业务终态,并按 sendStatus 判断是否真正投递成功",
|
||||
"use_when": [
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时"
|
||||
"发送命令返回 openTaskId 后需要确认投递结果时;只有 sendStatus=SUCCESS 才能宣称消息发送成功"
|
||||
],
|
||||
"avoid_when": [
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId"
|
||||
"没有 message send 真实返回的 openTaskId 时不要使用;openMessageId 和会话 ID 都不能代替 openTaskId;FAILED 不得当作接口成功"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat message query-send-status --open-task-id <openTaskId>"
|
||||
@@ -802,12 +802,12 @@
|
||||
]
|
||||
},
|
||||
"chat.remove_emoji_reaction": {
|
||||
"agent_summary": "移除指定消息上的表情回应",
|
||||
"agent_summary": "从同一条真实消息移除此前添加的同名表情回应",
|
||||
"use_when": [
|
||||
"需要取消此前添加的 emoji reaction 时"
|
||||
"需要取消此前添加的 emoji reaction 时;复用添加时的 conversation-id、msg-id 和 emoji"
|
||||
],
|
||||
"avoid_when": [
|
||||
"移除文字表情时使用 chat message remove-text-emotion"
|
||||
"移除文字表情时使用 chat message remove-text-emotion;不要猜测新的消息 ID 或表情名称"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat message remove-emoji --conversation-id <openConversationId> --msg-id <openMessageId> --emoji \"赞\""
|
||||
@@ -824,10 +824,10 @@
|
||||
"chat.remove_group_member": {
|
||||
"agent_summary": "从指定群聊移除成员",
|
||||
"use_when": [
|
||||
"群管理员明确要移除一个或多个普通成员时"
|
||||
"群管理员明确要移除一个或多个普通成员时;清理临时群只传本次加入的普通成员"
|
||||
],
|
||||
"avoid_when": [
|
||||
"移除机器人时使用 chat group members remove-bot"
|
||||
"移除机器人时使用 chat group members remove-bot;不要把建群者或群主加入 --users,也不要为了清理成员而转让群主"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat group members remove --id <openConversationId> --users userId1,userId2"
|
||||
@@ -1236,12 +1236,12 @@
|
||||
]
|
||||
},
|
||||
"chat.set_pin_message": {
|
||||
"agent_summary": "把指定消息设为会话置顶消息",
|
||||
"agent_summary": "使用同源的 openConversationId 和 openMessageId 把指定消息设为 Pin",
|
||||
"use_when": [
|
||||
"需要在会话中置顶一条已知消息时"
|
||||
"需要在会话中钉住一条已知消息时;若发送只返回 openTaskId,先确认发送终态,再从消息查询结果取得真实 openMessageId"
|
||||
],
|
||||
"avoid_when": [
|
||||
"取消置顶使用 chat message unset-pin-msg"
|
||||
"取消 Pin 使用 chat message unset-pin-msg;置顶到会话顶部使用 set-top-msg,置顶整个会话使用 chat set-top"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat message set-pin-msg --open-conversation-id <openConversationId> --msg-id <openMessageId>"
|
||||
@@ -1316,12 +1316,12 @@
|
||||
]
|
||||
},
|
||||
"chat.unset_pin_message": {
|
||||
"agent_summary": "取消指定消息的会话置顶",
|
||||
"agent_summary": "使用设置 Pin 时的同源会话和消息 ID 取消指定消息的 Pin",
|
||||
"use_when": [
|
||||
"需要移除一条已知置顶消息时"
|
||||
"需要移除一条已知 Pin 消息时;复用 set-pin-msg 成功时的 openConversationId 和 openMessageId"
|
||||
],
|
||||
"avoid_when": [
|
||||
"新增置顶使用 chat message set-pin-msg"
|
||||
"新增 Pin 使用 chat message set-pin-msg;不要改用 unset-top-msg 或 chat set-top --off"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat message unset-pin-msg --open-conversation-id <openConversationId> --msg-id <openMessageId>"
|
||||
@@ -1378,10 +1378,10 @@
|
||||
"chat.update_group_icon": {
|
||||
"agent_summary": "使用真实媒体 ID 更新群头像",
|
||||
"use_when": [
|
||||
"已有上传后的头像 mediaId 并要修改群头像时"
|
||||
"已有可用于群头像接口的真实 mediaId 并要修改群头像时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"没有真实可用 mediaId 时先完成媒体上传"
|
||||
"本地图片路径、钉盘 dentryId、聊天文件 uploadKey 都不能代替 icon-media-id;当前 CLI 无本地图片转群头像 mediaId 的能力,缺少 mediaId 时应如实报告能力边界"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat group update-icon --group <openConversationId> --icon-media-id @mediaId"
|
||||
@@ -1418,10 +1418,10 @@
|
||||
"chat.update_group_settings": {
|
||||
"agent_summary": "更新指定群聊的一项设置开关",
|
||||
"use_when": [
|
||||
"需要调整 searchable、入群验证或群权限等设置时"
|
||||
"需要调整 searchable、入群验证、@所有人权限等群级设置时;修改后用群信息或设置查询核对终态"
|
||||
],
|
||||
"avoid_when": [
|
||||
"全员禁言和成员禁言使用专门的 mute 命令"
|
||||
"全员禁言和成员禁言使用专门的 mute 命令;当前用户自己的会话置顶或免打扰使用 group user-settings set"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat group update-settings --group <openConversationId> --setting-key searchable --status 1"
|
||||
@@ -2845,12 +2845,12 @@
|
||||
]
|
||||
},
|
||||
"chat.shortcut_messages_query_send_status": {
|
||||
"agent_summary": "查询消息发送状态",
|
||||
"agent_summary": "查询消息发送任务的业务终态,只有 sendStatus=SUCCESS 才表示真正发送成功",
|
||||
"use_when": [
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id。"
|
||||
"当你发消息后拿到 openTaskId、想确认这条消息是否发送成功时使用;只读返回发送状态,需传 --open-task-id;FAILED 必须如实报告,不能因外层 success=true 宣称成功。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令;不要把 openTaskId 当 openMessageId 做 Pin、置顶或表情操作"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat +messages-query-send-status --open-task-id <openTaskId>"
|
||||
@@ -3142,10 +3142,10 @@
|
||||
"chat.batch_query_group_chat_settings": {
|
||||
"agent_summary": "批量查询当前用户自己的群会话设置(置顶/免打扰/群昵称/群备注)",
|
||||
"use_when": [
|
||||
"用户说 看下这些群我的置顶和免打扰设置"
|
||||
"用户说看下这些群中我自己的置顶和免打扰设置;修改前先查询并保存原值用于恢复"
|
||||
],
|
||||
"avoid_when": [
|
||||
"管理员级群功能开关用 chat group update-settings"
|
||||
"管理员级群功能开关用 chat group update-settings;查询结果是当前用户视角,不代表群级设置"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat group user-settings query --groups cid1,cid2 --format json"
|
||||
@@ -3161,10 +3161,10 @@
|
||||
"chat.batch_update_group_chat_settings": {
|
||||
"agent_summary": "批量更新当前用户自己的群会话设置(置顶/免打扰/群昵称/群备注)",
|
||||
"use_when": [
|
||||
"用户说 把这些群都设为免打扰/置顶"
|
||||
"用户说把这些群都设为免打扰或置顶;items 中只写需要改变的当前用户设置,完成后再次 query 验证"
|
||||
],
|
||||
"avoid_when": [
|
||||
"单个群昵称优先 chat group update-nick"
|
||||
"单个群昵称优先 chat group update-nick;不要用本命令修改 searchable、@所有人权限等群级开关;临时测试结束时按修改前 query 的真实值恢复"
|
||||
],
|
||||
"examples": [
|
||||
"dws chat group user-settings set --items '[{\"openConversationId\":\"cid1\",\"top\":true,\"mute\":false}]' --format json"
|
||||
|
||||
@@ -82,22 +82,22 @@
|
||||
]
|
||||
},
|
||||
"doc.create_document": {
|
||||
"agent_summary": "创建一篇新的在线文档",
|
||||
"agent_summary": "在默认根目录、文档文件夹或知识库根创建带可选初始内容的 adoc",
|
||||
"use_when": [
|
||||
"用户要新建一篇文字在线文档(adoc),可空文档或带初始 Markdown 时",
|
||||
"创建到指定文件夹 --folder、知识库根 --workspace,或默认「我的文档」根目录时"
|
||||
"用户要新建文字在线文档(adoc),可空文档或用 --content-file 写入明确要求的初始 Markdown/JSONML;用户显式要求正文 H1 时必须保留,不能用 --name 替代",
|
||||
"已知目标文档文件夹 --folder 或知识库 --workspace;长 Markdown 仍直接使用本命令,由原生写入管道自动分片;返回 nodeId 绑定同一请求的后续 list/insert/export,后续可观察操作仍须分别执行"
|
||||
],
|
||||
"avoid_when": [
|
||||
"创建表格/脑图/白板/多维表/演示改用 dws wiki node create --type <type>(勿用 doc create)",
|
||||
"在知识库建空节点实体也可用 wiki node create;本命令侧重可写初始内容的 adoc",
|
||||
"导入本地 Word/Markdown 为在线文档改用 dws doc import(若可用)或 upload --convert"
|
||||
"导入本地 Word/Markdown 为在线文档用 dws doc import,并显式提供 --folder 或 --workspace;不要用已迁移的 doc upload --convert"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc create --name \"项目周报\" --format json",
|
||||
"dws doc create --name \"Q1 总结\" --content \"# Q1 总结\" --folder <FOLDER_ID> --format json"
|
||||
"dws doc create --name \"Q1 总结\" --content-file ./q1.md --workspace <WORKSPACE_ID> --format json"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐当前 Cobra 原生内容写入管道、显式正文 H1、同请求新 nodeId 绑定和可观察块操作边界;强调长内容无需外部分片,不改变命令身份、参数或安全事实。",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=doc.create_document",
|
||||
"cobra-help:dws doc create",
|
||||
@@ -205,7 +205,7 @@
|
||||
"用户明确要求用 doc delete 兼容入口将文档/文件移入回收站,且已确认目标时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"常规文件删除优先 dws drive delete;本入口仅为兼容",
|
||||
"常规文件删除使用 dws drive delete;不要继续选择已弃用的 doc delete 兼容入口",
|
||||
"用户未确认或目标不清时不要删",
|
||||
"删块用 doc block delete;删评论用 doc comment delete"
|
||||
],
|
||||
@@ -250,10 +250,11 @@
|
||||
"doc.download_doc_attachment": {
|
||||
"agent_summary": "获取文档附件的临时下载链接",
|
||||
"use_when": [
|
||||
"获取文档正文中附件的临时下载 URL(resourceId 来自 block list attachment)时"
|
||||
"获取文档正文中附件的临时下载 URL 时;resourceId 必须来自本次 block list 返回的 attachment 块"
|
||||
],
|
||||
"avoid_when": [
|
||||
"下载钉盘普通文件用 drive download;导出在线文档用 doc export"
|
||||
"下载钉盘普通文件用 drive download;导出在线文档用 doc export",
|
||||
"不要把 blockId/nodeId 当作 resourceId,也不要编造 --output:本命令返回临时 downloadUrl"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc media download --node <DOC_ID> --resource-id <RESOURCE_ID> --format json"
|
||||
@@ -295,12 +296,12 @@
|
||||
"doc.get_document_content": {
|
||||
"agent_summary": "读取完整文档内容,或按 outline/range/section/tags 获取 JSONML fragment",
|
||||
"use_when": [
|
||||
"用户要读取钉钉在线文字文档(adoc)正文(Markdown)时",
|
||||
"用户直接粘贴文档 URL 且无其他指令时(默认读内容)",
|
||||
"已由 drive info 确认 extension=adoc,用户要读取正文(Markdown)时",
|
||||
"用户提供已知 adoc nodeId/URL,且要读取内容或抽取指定章节时",
|
||||
"只需标题大纲、指定块区间/单块或特定 JSONML tags 时使用 --content-format jsonml 与 --scope"
|
||||
],
|
||||
"avoid_when": [
|
||||
"非 adoc(表格/多维表/普通文件)不要用本命令;先 doc info 再路由",
|
||||
"原始 alidocs URL 类型未知或目标不是 adoc 时先用 drive info 探测并路由;表格/多维表/普通文件不要用本命令",
|
||||
"要元信息用 doc info;要块结构用 doc block list",
|
||||
"Markdown 为有损投影:保形复制模板请用 doc copy,不要 read→create"
|
||||
],
|
||||
@@ -309,7 +310,7 @@
|
||||
"dws doc read --node <DOC_ID> --content-format jsonml --scope outline --max-depth 3"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐 doc Skill 与 doc-info reference 的 extension 预检边界,并保留 Markdown/JSONML scope 的真实 Cobra 契约。",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=doc.get_document_content",
|
||||
"cobra-help:dws doc read",
|
||||
@@ -321,22 +322,23 @@
|
||||
]
|
||||
},
|
||||
"doc.get_document_info": {
|
||||
"agent_summary": "获取文档元信息(标题/类型/创建者/权限等)",
|
||||
"agent_summary": "在已确认是 ALIDOC 后读取文档专属元信息",
|
||||
"use_when": [
|
||||
"用户要查看文档/节点元信息(标题、类型、创建者、权限)时",
|
||||
"准备读内容前必须先看 contentType/extension 以路由到 read/sheet/aitable/download 时"
|
||||
"drive info 已确认是 ALIDOC,用户还要标题、创建者、权限或 docUrl 等文档专属元信息时",
|
||||
"创建响应只有 nodeId、缺少 docUrl,需要补查文档链接时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"已确认是 adoc 且只要正文改用 dws doc read",
|
||||
"原始 alidocs URL 的类型探测、extension 路由或可靠 fileSize 使用 dws drive info;不要先猜是文档",
|
||||
"已确认是 adoc 且只要正文时改用 dws doc read",
|
||||
"只要目录列表改用 dws drive list / wiki node list",
|
||||
"需要可靠文件大小 fileSize 时改用 dws drive info;文档元信息接口可能不返回大小"
|
||||
"普通文件、电子表格或 AI 表格不使用本命令"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc info --node <DOC_ID> --format json",
|
||||
"dws doc info --node \"https://alidocs.dingtalk.com/i/nodes/<DOC_UUID>\" --format json"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐 drive info 统一类型探测入口与 doc info 的文档专属补查职责;不改变 node 参数或接口绑定。",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=doc.get_document_info",
|
||||
"cobra-help:dws doc info",
|
||||
@@ -350,10 +352,10 @@
|
||||
"doc.import_get": {
|
||||
"agent_summary": "根据 taskId 查询文档导入任务的执行结果",
|
||||
"use_when": [
|
||||
"查询文档导入任务结果(已有 taskId,导入超时/中断后兜底)时"
|
||||
"仅在 doc import 已返回真实 taskId 且自动轮询超时/中断后,查询导入任务结果时"
|
||||
],
|
||||
"avoid_when": [
|
||||
"发起导入用 doc import(若入口可用);不要用本命令代替导入"
|
||||
"发起导入用 doc import;不要先调用 import get,也不要使用示例占位 taskId"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc import get --task-id <TASK_ID> --format json"
|
||||
@@ -371,11 +373,14 @@
|
||||
"doc.insert_document_block": {
|
||||
"agent_summary": "向文档插入块元素",
|
||||
"use_when": [
|
||||
"在文档中插入新块(段落/标题等);简单场景用 --text/--heading,复杂块用 --element JSON"
|
||||
"用户明确要求在文档中插入新块(段落/标题/列表等)时;简单段落/标题用 --text/--heading,复杂块用 --element JSON",
|
||||
"用户要求有序列表块时使用 JSONML p.list.isOrdered=true(同一 listId)或等价原生 orderedList 结构,不能用普通 Markdown 代替",
|
||||
"相对插入时先 block list 获取真实 blockId,再同时传 --ref-block 与 --where before|after"
|
||||
],
|
||||
"avoid_when": [
|
||||
"整篇追加 Markdown 优先 doc update --mode append",
|
||||
"插入本地文件附件优先 doc media insert",
|
||||
"仅在用户没有指定块操作、只要整篇追加 Markdown 时优先 doc update --mode append;显式 insert/标题块/列表块不能折叠",
|
||||
"插入本地文件附件优先 doc media insert;不要为普通附件手写 attachment JSON",
|
||||
"整段 Markdown 或长内容优先 doc update --mode append,不要拆成大量 block insert",
|
||||
"删块用 block delete;改已有块用 block update"
|
||||
],
|
||||
"examples": [
|
||||
@@ -383,7 +388,7 @@
|
||||
"dws doc block insert --node <DOC_ID> --heading \"二级标题\" --level 2 --format json"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐显式 block insert 的可观察操作语义和有序列表原生结构;不改变命令身份、参数契约、接口绑定或 Runtime 门禁。",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=doc.insert_document_block",
|
||||
"cobra-help:dws doc block insert",
|
||||
@@ -420,7 +425,7 @@
|
||||
"doc.list_document_blocks": {
|
||||
"agent_summary": "查询文档一级块元素列表",
|
||||
"use_when": [
|
||||
"查看文档一级块结构、拿 blockId,供 insert/update/delete 或划词评论定位时"
|
||||
"查看文档一级块结构、拿 blockId,供 insert/update/delete 或划词评论定位时;用户在显式工作流中点名 list 时必须真实执行并使用返回结果"
|
||||
],
|
||||
"avoid_when": [
|
||||
"只要全文 Markdown 用 doc read",
|
||||
@@ -431,7 +436,7 @@
|
||||
"dws doc block list --node <DOC_ID> --start-index 0 --end-index 5 --format json"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐 block list 的显式可观察步骤和真实 blockId 上下文传递;不改变命令身份、参数契约或接口绑定。",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=doc.list_document_blocks",
|
||||
"cobra-help:dws doc block list",
|
||||
@@ -606,10 +611,11 @@
|
||||
"doc.reply_comment": {
|
||||
"agent_summary": "回复文档评论",
|
||||
"use_when": [
|
||||
"回复已有评论(文字、可 @用户/@群,或 --emoji 表情);commentKey 来自 list/create"
|
||||
"回复已有评论(文字、可 @用户/@群,或 --emoji 表情);commentKey 必须来自本次 list/create 返回"
|
||||
],
|
||||
"avoid_when": [
|
||||
"新建评论用 create/create-inline;删评论用 delete"
|
||||
"新建评论用 create/create-inline;删评论用 delete",
|
||||
"不要把 commentId、blockId 或示例占位符传给 --comment-key"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc comment reply --node <DOC_ID> --comment-key <COMMENT_KEY> --content \"同意\" --mentioned-open-conversation-id <openConversationId> --format json",
|
||||
@@ -739,15 +745,17 @@
|
||||
]
|
||||
},
|
||||
"doc.update_document": {
|
||||
"agent_summary": "更新文档内容(追加 / 覆盖;覆盖需 --yes)",
|
||||
"agent_summary": "用原生自动分片管道追加或整篇覆盖 adoc 内容",
|
||||
"use_when": [
|
||||
"用户要向已有 adoc 追加内容时用 --mode append(更安全)",
|
||||
"用户要向已有 adoc 追加长、多行或文件内容时用 --mode append + --content-file;CLI 自动分片",
|
||||
"用户明确要求整篇覆盖替换时用 --mode overwrite(破坏性)",
|
||||
"append 且要插到第 N 个 block 前时加 --index N(先 block list)"
|
||||
"append 且要插到第 N 个 block 前时加 --index N(先 block list)",
|
||||
"长文本、多行内容或表格优先写入 UTF-8 文件并使用 --content-file,避免 shell 转义损坏"
|
||||
],
|
||||
"avoid_when": [
|
||||
"目标不是 adoc 或只要改单个块时改用 doc block update",
|
||||
"覆盖模式用户未确认前不要执行;可先 --dry-run 预览",
|
||||
"覆盖模式用户未确认前不要执行;先 --dry-run 预览,确认后再加 --yes",
|
||||
"--content 与 --content-file 二选一;--index 仅用于 mode=append",
|
||||
"创建新文档用 doc create,不要用 update 冒充创建"
|
||||
],
|
||||
"examples": [
|
||||
@@ -755,7 +763,7 @@
|
||||
"dws doc update --node <DOC_ID> --content-file ./body.md --mode overwrite --dry-run"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐 Runtime 的 10000 字符自动分片、append/overwrite 动态门禁与根 Skill 的写后回读流程;不改变参数和安全事实。",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=doc.update_document",
|
||||
"cobra-help:dws doc update",
|
||||
@@ -769,11 +777,12 @@
|
||||
"doc.update_document_block": {
|
||||
"agent_summary": "更新文档中的指定块",
|
||||
"use_when": [
|
||||
"修改已有块的文本/标题/样式(已知 blockId)时"
|
||||
"修改已有块的文本/标题/样式时;blockId 必须来自目标文档本次 block list 结果"
|
||||
],
|
||||
"avoid_when": [
|
||||
"插入新块用 block insert;删除用 block delete",
|
||||
"改文档显示名用 rename;整篇覆盖用 update overwrite"
|
||||
"改文档显示名用 rename;整篇覆盖用 update overwrite",
|
||||
"不要跨文档复用 blockId,也不要把 nodeId/commentKey 当作 blockId"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc block update --node <DOC_ID> --block-id <BLOCK_ID> --text \"新内容\" --format json"
|
||||
@@ -905,14 +914,14 @@
|
||||
"当你只记得文档的标题或主题词、需要先定位到某篇钉钉文档拿到它的 nodeId/URL 以便后续阅读或编辑时使用;可按关键词、扩展名、创建/访问时间、创建者等条件过滤,不传关键词则返回最近访问的文档,返回匹配的文档列表。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只有一个关键词且只要紧凑标题/URL/type/token 投影时优先 +find-doc;目标已给 nodeId/URL 时不要再搜索"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +search --query \"会议纪要\"",
|
||||
"dws doc +search --extensions pdf,docx"
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工区分 +search 的丰富过滤/最近访问能力与 +find-doc 的单关键词紧凑投影,避免两个同源搜索 Shortcut 互相争抢。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_search",
|
||||
"cobra-help:dws doc +search",
|
||||
@@ -925,14 +934,14 @@
|
||||
"当你已知某个文档文件夹或知识库的 ID、想浏览它下面直接包含的文档与子文件夹(不递归深层)以便逐层导航时使用;输入 folder 或 workspace,返回该层级的子节点列表。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"全局或钉盘目录浏览优先 drive list,知识库节点树优先 wiki node list;本 Shortcut 只列已知 doc folder/workspace 的直接子节点"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +list --folder DOC_FOLDER_NODE_ID",
|
||||
"dws doc +list --workspace WS_ID --limit 20"
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工对齐 doc/drive/wiki 产品边界:保留已知文档文件夹/知识库的直接子节点投影,不替代常规目录管理。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_list",
|
||||
"cobra-help:dws doc +list",
|
||||
@@ -945,13 +954,13 @@
|
||||
"当你想保留原件、在另一个文件夹或知识库里生成一份文档/文件副本(例如以某篇文档为模板另存)时使用;输入源 node 与目标 folder/workspace,会实际创建一个副本。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"常规文件管理优先 drive copy;要搬走原件用 move;复制后必须从真实返回取副本 nodeId,禁止继续编辑源文档"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +copy --node DOC_ID --folder TARGET_FOLDER_NODE_ID"
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工对齐保形复制、真实副本 ID 与 drive 文件管理边界;保留 Runtime user_required 门禁。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_copy",
|
||||
"cobra-help:dws doc +copy",
|
||||
@@ -964,13 +973,13 @@
|
||||
"当你要整理文档归属、把某篇文档/文件从当前位置挪到另一个文件夹或知识库(原位置不再保留)时使用;输入 node 与目标 folder/workspace,会实际改变文件的存放位置。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"常规文件管理优先 drive move;要保留原位置副本用 copy;目标位置不明确或未确认时不要移动"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +move --node DOC_ID --folder TARGET_FOLDER_NODE_ID"
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工对齐 move 的原位置消失语义、目标位置确认与 drive 文件管理边界;保留 Runtime user_required 门禁。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_move",
|
||||
"cobra-help:dws doc +move",
|
||||
@@ -1041,13 +1050,13 @@
|
||||
"当你想把在线文档导出成 docx/markdown/pdf 文件(例如离线保存或外发)时使用;这是异步任务的第一步,输入 node 与 export-format 提交导出,返回 jobId,随后用 +export-get 轮询结果。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"用户要直接拿到本地 docx/markdown/pdf 文件时使用一体化 dws doc export;不要默认手工编排 submit/get 轮询"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +export-submit --node DOC_ID --export-format markdown"
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工对齐 atomic doc export 的一体化提交/轮询/下载路径,将 +export-submit 限定为明确异步控制场景。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_export_submit",
|
||||
"cobra-help:dws doc +export-submit",
|
||||
@@ -1117,13 +1126,13 @@
|
||||
"当文档被误改、你想把它整体恢复到某个历史版本时使用;先用 +version-list 找到目标版本号,再输入 node 与 version,会实际把文档内容覆盖回该版本,属于高风险写操作,需谨慎确认。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"只查看历史用 +version-list,只保存当前快照用 +version-save;版本号未核实、用户未确认或只需改单块时不要回滚"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +version-revert --node DOC_ID --version 3"
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工对齐 version list → 明确确认 → revert → 回读的 ID 与高风险边界;不改变 typed_yes 安全门禁。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_version_revert",
|
||||
"cobra-help:dws doc +version-revert",
|
||||
@@ -1174,14 +1183,14 @@
|
||||
"当你只想往一篇钉钉文档的最后面补一段文字、又不想动原有内容时使用;内部用文档更新的“追加(append)”模式,把你给的文本安全地拼到文档末尾,不需要你先去查文档块列表、算末尾位置或手工拼块结构。会真实写入文档内容。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"长、多行、表格或文件内容使用 doc update --mode append --content-file;指定位置或富结构使用 block insert;不要跳过 user_required 确认"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +doc-append --doc DOC_ID --text \"补充说明:本方案已评审通过。\"",
|
||||
"dws doc +doc-append --doc \"https://alidocs.dingtalk.com/i/nodes/<DOC_UUID>\" --text \"追加一行备注\""
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工将 Shortcut 限定为短纯文本末尾追加,并对齐 user_required 与写后回读;长内容交给原生自动分片管道。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_doc_append",
|
||||
"cobra-help:dws doc +doc-append",
|
||||
@@ -1194,14 +1203,14 @@
|
||||
"当你只记得云文档标题或内容里的某个关键词,想快速按关键词找到匹配的文档、拿到它的标题、URL、类型和 token 以便后续查看或编辑,却不想拿到一大坨原始字段时使用;内部调用云文档的 search_documents 工具,把 --query 作为搜索关键词(keyword),可选地用 --limit 限制返回条数(pageSize),再在本地把每条命中结果精简为「标题、URL、类型、token」四个字段后打印。这是纯只读操作,只做搜索与本地投影,不会创建、修改或删除任何文档;未命中时提示「没搜到文档」。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"需要最近访问、extension/时间/创建者等组合过滤时使用 +search;目标已给 nodeId/URL 时直接进入 info/read,不再搜索"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +find-doc --query 季度汇报",
|
||||
"dws doc +find-doc --query 合同 --limit 10"
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工将 +find-doc 定位为高频单关键词紧凑投影,并与 +search 的丰富过滤能力做互斥路由。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_find_doc",
|
||||
"cobra-help:dws doc +find-doc",
|
||||
@@ -1214,13 +1223,13 @@
|
||||
"当你手上已经有一个文档链接、想直接私信发给某个人而不必先查 userId 时使用;内部先按姓名搜通讯录解析出唯一用户,再用 openDingTalkId 把链接拼成一条 Markdown 消息发出去,姓名匹配到多人时会列出候选让你区分。只发链接、不读取或改动文档本身,会真实发出消息。"
|
||||
],
|
||||
"avoid_when": [
|
||||
"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"
|
||||
"同名人员未消歧、缺少真实文档 URL 或用户未确认时不要发送;本命令不授予文档权限,授权应走 drive permission"
|
||||
],
|
||||
"examples": [
|
||||
"dws doc +share-doc --to 张三 --url https://docs.dingtalk.com/xxx --note \"帮忙过一下\""
|
||||
],
|
||||
"reviewed": true,
|
||||
"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.",
|
||||
"review_reason": "人工对齐人员消歧、真实 docUrl、user_required 消息发送门禁与文档权限边界。",
|
||||
"source_refs": [
|
||||
"internal/cli/schema_command_registry.json#doc.shortcut_share_doc",
|
||||
"cobra-help:dws doc +share-doc",
|
||||
|
||||
@@ -910,12 +910,15 @@
|
||||
"agent_summary": "上传本地文件到钉盘或文档空间,或按节点 ID 确认覆盖已有文件",
|
||||
"use_when": [
|
||||
"用户要把本地文件上传到钉盘/我的文件(首选一条命令自动完成凭证+PUT+提交)时",
|
||||
"上传到知识库/文档空间时加 --workspace;需要转在线文档时加 --convert",
|
||||
"用户明确要求只上传或暂存附件、暂时不要发送到任何聊天会话时",
|
||||
"用户只要保留原始文件供存储或下载时使用;上传到知识库/文档空间时可加 --workspace",
|
||||
"用户明确要求用本地文件替换已有钉盘/文档空间文件时传 --node;该模式会覆盖远端内容并要求确认"
|
||||
],
|
||||
"avoid_when": [
|
||||
"用户要把文件发送到群聊或单聊时使用 chat message send --msg-type file --file-path;drive upload 不会发送聊天消息",
|
||||
"常规场景不要拆成 upload-info + 手动 PUT + commit;仅自定义流式上传才用三步",
|
||||
"要把文件作为文档正文附件插入改用 dws doc media insert",
|
||||
"Word/Excel/Markdown 要求上传后在线编辑、大家直接在线改或转钉钉文档时改用 dws doc import;普通 drive upload 返回 docx/xlsx 文件不能宣称可在线编辑",
|
||||
"用户明确说文档空间且走 doc 兼容入口时可用 dws doc upload,默认仍推荐本命令",
|
||||
"用户没有明确同意替换目标文件时不要使用 --node;新建上传应使用 --folder 或目标根目录"
|
||||
],
|
||||
@@ -924,7 +927,7 @@
|
||||
"dws drive upload --file ./README.md --node <dentryUuid> --format json"
|
||||
],
|
||||
"reviewed": true,
|
||||
"review_reason": "人工审阅:结合本机 dws schema 实时 MCP description/parameters(经 interface_ref)、产品 Skill、Cobra Long 与 Runtime 确认门禁,按飞书风格重写选型文案;不改变命令身份、参数契约或接口绑定。",
|
||||
"review_reason": "人工对齐普通文件存储与在线文档导入边界:在线编辑需求硬路由 doc import,drive upload 不承诺转换结果;不改变命令身份、参数契约或接口绑定。",
|
||||
"source_refs": [
|
||||
"CommandRegistry:canonical_path=drive.upload",
|
||||
"cobra-help:dws drive upload",
|
||||
|
||||
@@ -363,6 +363,24 @@ func TestSuggestBusinessHintChatRecovery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuggestBusinessHintDocRecovery(t *testing.T) {
|
||||
cases := []struct{ message, want string }{
|
||||
{"nodeId not found", "drive search"},
|
||||
{"blockId 不存在", "block list"},
|
||||
{"commentKey invalid", "comment list/create"},
|
||||
{"resourceId not found", "attachment"},
|
||||
{"workspaceId invalid", "wiki space list"},
|
||||
{"folderId invalid", "dentryId"},
|
||||
{"templateId not found", "template search"},
|
||||
{"version 不存在", "version list"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got := SuggestBusinessHint(map[string]any{"message": tc.message}); !strings.Contains(got, tc.want) {
|
||||
t.Errorf("SuggestBusinessHint(%q) = %q, want containing %q", tc.message, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoveragePATSerializationAndPolicyEdges(t *testing.T) {
|
||||
oldHost := hostControlProvider
|
||||
oldBrowser := patBrowserProvider
|
||||
|
||||
@@ -340,6 +340,22 @@ func suggestForBusinessErrorText(body map[string]any) string {
|
||||
switch {
|
||||
case strings.Contains(msg, "搜索内容不能为空"):
|
||||
return "请提供非空搜索关键词: dws doc search --query \"关键词\""
|
||||
case strings.Contains(msg, "nodeId") && (strings.Contains(msg, "not found") || strings.Contains(msg, "不存在")):
|
||||
return "目标文档 nodeId 不存在或当前账号不可见。请用 dws drive search 或 dws wiki node search 重新获取真实 nodeId;不要复用示例占位符。"
|
||||
case strings.Contains(msg, "blockId") && (strings.Contains(msg, "not found") || strings.Contains(msg, "不存在")):
|
||||
return "目标 blockId 不存在或已变化。请重新执行 dws doc block list --node <DOC_ID> --format json,并从本次结果取 blockId。"
|
||||
case strings.Contains(msg, "commentKey") && (strings.Contains(msg, "invalid") || strings.Contains(msg, "不存在") || strings.Contains(msg, "not found")):
|
||||
return "commentKey 无效或评论已不存在。请从 dws doc comment list/create 的当前返回结果中提取 commentKey,不要使用 commentId 或占位符。"
|
||||
case strings.Contains(msg, "resourceId") && (strings.Contains(msg, "invalid") || strings.Contains(msg, "不存在") || strings.Contains(msg, "not found")):
|
||||
return "resourceId 无效。请用 dws doc block list 查找 attachment 块并读取真实 resourceId;不要把 blockId 当作 resourceId。"
|
||||
case strings.Contains(msg, "workspaceId") && (strings.Contains(msg, "invalid") || strings.Contains(msg, "不存在") || strings.Contains(msg, "not found")):
|
||||
return "workspaceId 无效或无权访问。请用 dws wiki space list --type myWikiSpace --format json 获取当前账号可用的 workspaceId。"
|
||||
case strings.Contains(msg, "folderId") && (strings.Contains(msg, "invalid") || strings.Contains(msg, "不存在") || strings.Contains(msg, "not found")):
|
||||
return "folderId 无效。doc 的 --folder 需要文档文件夹 nodeId/URL,不是 drive 的纯数字 dentryId;请重新列出目标空间节点。"
|
||||
case strings.Contains(msg, "templateId") && (strings.Contains(msg, "invalid") || strings.Contains(msg, "不存在") || strings.Contains(msg, "not found")):
|
||||
return "templateId 无效或模板不可见。请先用 dws doc template search --query <关键词> --format json 获取当前账号可用的真实 templateId。"
|
||||
case strings.Contains(msg, "version") && (strings.Contains(msg, "不存在") || strings.Contains(msg, "not found")):
|
||||
return "目标版本不存在。请先执行 dws doc version list --node <DOC_ID> --format json,并从当前版本列表选择可回滚版本。"
|
||||
case strings.Contains(msg, "User has no permission to access this email"):
|
||||
return "请确认邮箱地址正确,查看可用邮箱: dws mail mailbox list"
|
||||
case strings.Contains(msg, "频率超限") || strings.Contains(msg, "rate limit"):
|
||||
|
||||
+177
-2
@@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/cli"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -132,6 +133,8 @@ func recordQueryFetchAll(toolArgs map[string]any, pageLimit int) error {
|
||||
var allRecords []any
|
||||
page := 0
|
||||
lastCursor := ""
|
||||
seenCursors := map[string]struct{}{}
|
||||
stopReason := ""
|
||||
|
||||
for {
|
||||
page++
|
||||
@@ -201,6 +204,19 @@ func recordQueryFetchAll(toolArgs map[string]any, pageLimit int) error {
|
||||
lastCursor = ""
|
||||
break
|
||||
}
|
||||
if current, _ := toolArgs["cursor"].(string); cursor == current {
|
||||
lastCursor = cursor
|
||||
stopReason = "cursor_not_advanced"
|
||||
fmt.Fprintf(os.Stderr, "[pagination] cursor did not advance (%q), stopping to avoid an infinite loop. Resume with --cursor %q after checking the service response.\n", cursor, cursor)
|
||||
break
|
||||
}
|
||||
if _, exists := seenCursors[cursor]; exists {
|
||||
lastCursor = cursor
|
||||
stopReason = "cursor_cycle_detected"
|
||||
fmt.Fprintf(os.Stderr, "[pagination] cursor cycle detected (%q), stopping to avoid an infinite loop. Resume with --cursor %q after checking the service response.\n", cursor, cursor)
|
||||
break
|
||||
}
|
||||
seenCursors[cursor] = struct{}{}
|
||||
|
||||
// Check page limit (0 = unlimited)
|
||||
if pageLimit > 0 && page >= pageLimit {
|
||||
@@ -223,6 +239,10 @@ func recordQueryFetchAll(toolArgs map[string]any, pageLimit int) error {
|
||||
if lastCursor != "" {
|
||||
mergedData["cursor"] = lastCursor
|
||||
mergedData["hasMore"] = true
|
||||
if stopReason != "" {
|
||||
mergedData["incomplete"] = true
|
||||
mergedData["stopReason"] = stopReason
|
||||
}
|
||||
} else {
|
||||
mergedData["hasMore"] = false
|
||||
}
|
||||
@@ -781,6 +801,20 @@ func isAitableRetryableError(err error) bool {
|
||||
}
|
||||
msg := strings.ToLower(err.Error())
|
||||
|
||||
// 确定性业务错误优先于外层 category/retryable 标记,避免对不存在的资源、
|
||||
// 参数错误或权限错误做无意义重试。部分 MCP 响应会同时携带
|
||||
// "category: internal" 或 "retryable: true",因此必须先判定终止类错误。
|
||||
nonRetryablePatterns := []string{
|
||||
"input_error", "user_error", "auth_error", "permission denied",
|
||||
"invalid parameter", "invalid argument", "bad request",
|
||||
"not found", "no record", "does not exist", "不存在",
|
||||
}
|
||||
for _, p := range nonRetryablePatterns {
|
||||
if strings.Contains(msg, p) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 网络瞬态错误
|
||||
retryablePatterns := []string{
|
||||
"timeout", "deadline exceeded", "connection reset",
|
||||
@@ -814,17 +848,154 @@ func isAitableRetryableError(err error) bool {
|
||||
func parseFieldsJSON(raw string) ([]any, error) {
|
||||
var fields []any
|
||||
if err := json.Unmarshal([]byte(raw), &fields); err == nil {
|
||||
return fields, nil
|
||||
return validateAitableCreateFields(fields)
|
||||
}
|
||||
var wrapper map[string]any
|
||||
if err := json.Unmarshal([]byte(raw), &wrapper); err == nil {
|
||||
if arr, ok := wrapper["fields"].([]any); ok {
|
||||
return arr, nil
|
||||
return validateAitableCreateFields(arr)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("--fields JSON parse failed: expect a JSON array [...]\n hint: example: '[{\"fieldName\":\"名称\",\"type\":\"text\"}]'")
|
||||
}
|
||||
|
||||
// validateAitableCreateFields catches stable field-contract mistakes before MCP dispatch
|
||||
// and returns a directly actionable correction. It intentionally does not normalize aliases:
|
||||
// silently changing a field type can alter business semantics.
|
||||
func validateAitableCreateFields(fields []any) ([]any, error) {
|
||||
if len(fields) > 15 {
|
||||
return nil, fmt.Errorf("--fields contains %d items; AI Table accepts at most 15 fields per request\n hint: split the fields into batches of 15 or fewer", len(fields))
|
||||
}
|
||||
for i, rawField := range fields {
|
||||
field, ok := rawField.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("--fields[%d] must be a JSON object\n hint: example: {\"fieldName\":\"状态\",\"type\":\"singleSelect\"}", i)
|
||||
}
|
||||
if _, hasAlias := field["fieldType"]; hasAlias {
|
||||
return nil, fmt.Errorf("--fields[%d] uses unsupported key fieldType\n hint: use fieldName + type, for example {\"fieldName\":\"状态\",\"type\":\"singleSelect\"}", i)
|
||||
}
|
||||
fieldName, nameOK := field["fieldName"].(string)
|
||||
if !nameOK || strings.TrimSpace(fieldName) == "" {
|
||||
return nil, fmt.Errorf("--fields[%d].fieldName must be a non-empty string\n hint: example: {\"fieldName\":\"任务名称\",\"type\":\"text\"}", i)
|
||||
}
|
||||
if strings.ContainsAny(fieldName, "\r\n") {
|
||||
return nil, fmt.Errorf("--fields[%d].fieldName must not contain line breaks\n hint: use a single-line field name with at most 100 characters", i)
|
||||
}
|
||||
if utf8.RuneCountInString(fieldName) > 100 {
|
||||
return nil, fmt.Errorf("--fields[%d].fieldName is %d characters; maximum is 100\n hint: shorten the field name before retrying", i, utf8.RuneCountInString(fieldName))
|
||||
}
|
||||
fieldType, typeOK := field["type"].(string)
|
||||
fieldType = strings.TrimSpace(fieldType)
|
||||
if !typeOK || fieldType == "" {
|
||||
return nil, fmt.Errorf("--fields[%d].type must be a non-empty string\n hint: common types: text, number, singleSelect, multipleSelect, date, user, attachment", i)
|
||||
}
|
||||
if strings.EqualFold(strings.TrimSpace(fieldType), "select") {
|
||||
return nil, fmt.Errorf("--fields[%d].type %q is not a valid AI Table field type\n hint: use \"singleSelect\" for single choice or \"multipleSelect\" for multiple choice; do not use \"select\"", i, fieldType)
|
||||
}
|
||||
if !aitableFieldTypes[fieldType] {
|
||||
return nil, fmt.Errorf("--fields[%d].type %q is unsupported\n hint: use a documented type such as text, number, singleSelect, multipleSelect, date, user, attachment, url, or richText", i, fieldType)
|
||||
}
|
||||
if fieldType == "primaryDoc" && i != 0 {
|
||||
return nil, fmt.Errorf("--fields[%d] uses primaryDoc outside the first column\n hint: primaryDoc is allowed only as --fields[0]", i)
|
||||
}
|
||||
config, hasConfig := field["config"]
|
||||
var configMap map[string]any
|
||||
if hasConfig {
|
||||
var configOK bool
|
||||
configMap, configOK = config.(map[string]any)
|
||||
if !configOK {
|
||||
return nil, fmt.Errorf("--fields[%d].config must be a JSON object\n hint: example: {\"options\":[{\"name\":\"高\"},{\"name\":\"低\"}]}", i)
|
||||
}
|
||||
}
|
||||
if err := validateAitableFieldConfig(i, fieldType, configMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
var aitableFieldTypes = map[string]bool{
|
||||
"text": true, "number": true, "singleSelect": true, "multipleSelect": true, "date": true,
|
||||
"currency": true, "user": true, "department": true, "group": true, "progress": true,
|
||||
"rating": true, "checkbox": true, "attachment": true, "url": true, "richText": true,
|
||||
"telephone": true, "email": true, "idCard": true, "barcode": true, "geolocation": true,
|
||||
"address": true, "primaryDoc": true, "formula": true, "filterUp": true, "lookup": true,
|
||||
"unidirectionalLink": true, "bidirectionalLink": true, "creator": true, "lastModifier": true,
|
||||
"createdTime": true, "lastModifiedTime": true,
|
||||
}
|
||||
|
||||
func validateAitableFieldConfig(index int, fieldType string, config map[string]any) error {
|
||||
if fieldType == "singleSelect" || fieldType == "multipleSelect" {
|
||||
if config == nil {
|
||||
return fmt.Errorf("--fields[%d].config.options is required for %s\n hint: use {\"options\":[{\"name\":\"选项A\"},{\"name\":\"选项B\"}]}", index, fieldType)
|
||||
}
|
||||
options, ok := config["options"].([]any)
|
||||
if !ok || len(options) == 0 {
|
||||
return fmt.Errorf("--fields[%d].config.options must be a non-empty JSON array\n hint: example: {\"options\":[{\"name\":\"高\"},{\"name\":\"低\"}]}", index)
|
||||
}
|
||||
for optionIndex, rawOption := range options {
|
||||
option, ok := rawOption.(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("--fields[%d].config.options[%d] must be a JSON object\n hint: each option must look like {\"name\":\"选项名\"}", index, optionIndex)
|
||||
}
|
||||
name, ok := option["name"].(string)
|
||||
if !ok || strings.TrimSpace(name) == "" {
|
||||
return fmt.Errorf("--fields[%d].config.options[%d].name must be a non-empty string\n hint: example: {\"name\":\"高\"}", index, optionIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
if config == nil {
|
||||
if fieldType == "formula" {
|
||||
return fmt.Errorf("--fields[%d].config.formula is required for formula\n hint: example: {\"formula\":\"[单价] * [数量]\"}", index)
|
||||
}
|
||||
if fieldType == "unidirectionalLink" || fieldType == "bidirectionalLink" {
|
||||
return fmt.Errorf("--fields[%d].config.linkedTableId is required for %s\n hint: get the target table ID first, then use {\"linkedTableId\":\"<TABLE_ID>\",\"multiple\":true}", index, fieldType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if formatter, ok := config["formatter"].(string); ok && formatter != "" {
|
||||
allowed := map[string]map[string]bool{
|
||||
"number": {"INT": true, "FLOAT_1": true, "FLOAT_2": true, "FLOAT_3": true, "FLOAT_4": true, "THOUSAND": true, "THOUSAND_FLOAT": true, "PERCENT": true, "PERCENT_FLOAT": true},
|
||||
"date": {"YYYY-MM-DD": true, "YYYY-MM-DD HH:mm": true, "YYYY-MM-DD HH:mm:ss": true, "YYYY/MM/DD": true, "YYYY/MM/DD HH:mm": true},
|
||||
"currency": {"INT": true, "FLOAT_1": true, "FLOAT_2": true, "FLOAT_3": true, "FLOAT_4": true},
|
||||
"progress": {"PERCENT": true},
|
||||
}
|
||||
if typeAllowed, applies := allowed[fieldType]; applies && !typeAllowed[formatter] {
|
||||
return fmt.Errorf("--fields[%d].config.formatter %q is invalid for %s\n hint: run 'dws aitable table create --help' and use a formatter listed for %s", index, formatter, fieldType, fieldType)
|
||||
}
|
||||
}
|
||||
if fieldType == "currency" {
|
||||
currency, _ := config["currencyType"].(string)
|
||||
currencies := map[string]bool{"CNY": true, "HKD": true, "USD": true, "EUR": true, "GBP": true, "MOP": true, "VND": true, "JPY": true, "KRW": true, "AED": true, "AUD": true, "BRL": true, "CAD": true, "CHF": true, "INR": true, "IDR": true, "MXN": true, "MYR": true, "PHP": true, "PLN": true, "RUB": true, "SGD": true, "THB": true, "TRY": true, "TWD": true}
|
||||
if currency != "" && !currencies[currency] {
|
||||
return fmt.Errorf("--fields[%d].config.currencyType %q is unsupported\n hint: use an ISO currency from table create --help, for example CNY, USD, EUR, JPY, or HKD", index, currency)
|
||||
}
|
||||
}
|
||||
if fieldType == "rating" {
|
||||
if max, ok := config["max"].(float64); ok && (max < 1 || max > 10) {
|
||||
return fmt.Errorf("--fields[%d].config.max must be between 1 and 10 for rating\n hint: example: {\"min\":1,\"max\":5,\"icon\":\"star\"}", index)
|
||||
}
|
||||
}
|
||||
if multiple, exists := config["multiple"]; exists {
|
||||
if _, ok := multiple.(bool); !ok {
|
||||
return fmt.Errorf("--fields[%d].config.multiple must be true or false\n hint: use a JSON boolean without quotes, for example {\"multiple\":false}", index)
|
||||
}
|
||||
}
|
||||
if fieldType == "formula" {
|
||||
formula, _ := config["formula"].(string)
|
||||
if strings.TrimSpace(formula) == "" {
|
||||
return fmt.Errorf("--fields[%d].config.formula is required for formula\n hint: example: {\"formula\":\"[单价] * [数量]\"}", index)
|
||||
}
|
||||
}
|
||||
if fieldType == "unidirectionalLink" || fieldType == "bidirectionalLink" {
|
||||
linkedTableID, _ := config["linkedTableId"].(string)
|
||||
if strings.TrimSpace(linkedTableID) == "" {
|
||||
return fmt.Errorf("--fields[%d].config.linkedTableId is required for %s\n hint: get the target table ID first, then use {\"linkedTableId\":\"<TABLE_ID>\",\"multiple\":true}", index, fieldType)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveFormUpdateTitle 折叠 form update 的 --title / --name 别名为单个 title 值。
|
||||
// 优先 --title,未设置时回退到 --name;两者都未传返回 ""。
|
||||
// 抽出独立函数便于单测覆盖。
|
||||
@@ -1370,6 +1541,10 @@ config 结构参考:
|
||||
} else {
|
||||
return fmt.Errorf("must specify either --fields OR both --name and --type")
|
||||
}
|
||||
fields, err := validateAitableCreateFields(fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
baseID, err := mustFlagOrFallback(cmd, "base-id", "base")
|
||||
if err != nil {
|
||||
|
||||
@@ -88,6 +88,87 @@ func TestCrossPlatformCoverageAitableRetryWrappersExhaustAndRecover(t *testing.T
|
||||
}
|
||||
}
|
||||
|
||||
func TestAitableTableCreateInvalidFieldContractDoesNotDispatchMCP(t *testing.T) {
|
||||
oldDeps, oldArgs := deps, os.Args
|
||||
t.Cleanup(func() { deps, os.Args = oldDeps, oldArgs })
|
||||
|
||||
longName := strings.Repeat("字", 101)
|
||||
manyFields := make([]string, 16)
|
||||
for i := range manyFields {
|
||||
manyFields[i] = fmt.Sprintf(`{"fieldName":"F%d","type":"text"}`, i)
|
||||
}
|
||||
cases := []struct {
|
||||
name string
|
||||
fields string
|
||||
hint string
|
||||
}{
|
||||
{"field must be object", `[1]`, "must be a JSON object"},
|
||||
{"fieldType key", `[{"fieldName":"状态","fieldType":"singleSelect"}]`, "fieldName + type"},
|
||||
{"missing fieldName", `[{"type":"text"}]`, "fieldName must be"},
|
||||
{"empty fieldName", `[{"fieldName":" ","type":"text"}]`, "fieldName must be"},
|
||||
{"fieldName line break", `[{"fieldName":"任务\n名称","type":"text"}]`, "must not contain line breaks"},
|
||||
{"fieldName too long", fmt.Sprintf(`[{"fieldName":%q,"type":"text"}]`, longName), "maximum is 100"},
|
||||
{"missing type", `[{"fieldName":"任务"}]`, "type must be"},
|
||||
{"unknown type", `[{"fieldName":"任务","type":"string"}]`, "type \"string\" is unsupported"},
|
||||
{"select alias", `[{"fieldName":"状态","type":"select"}]`, "singleSelect"},
|
||||
{"too many fields", `[` + strings.Join(manyFields, ",") + `]`, "at most 15"},
|
||||
{"config scalar", `[{"fieldName":"任务","type":"text","config":"bad"}]`, "config must be a JSON object"},
|
||||
{"single select missing options", `[{"fieldName":"状态","type":"singleSelect"}]`, "config.options is required"},
|
||||
{"select options not array", `[{"fieldName":"状态","type":"singleSelect","config":{"options":{}}}]`, "non-empty JSON array"},
|
||||
{"select options empty", `[{"fieldName":"状态","type":"singleSelect","config":{"options":[]}}]`, "non-empty JSON array"},
|
||||
{"select option scalar", `[{"fieldName":"状态","type":"singleSelect","config":{"options":["高"]}}]`, "must be a JSON object"},
|
||||
{"select option empty name", `[{"fieldName":"状态","type":"singleSelect","config":{"options":[{"name":""}]}}]`, "name must be a non-empty string"},
|
||||
{"number formatter", `[{"fieldName":"金额","type":"number","config":{"formatter":"CURRENCY_YUAN"}}]`, "invalid for number"},
|
||||
{"date formatter", `[{"fieldName":"日期","type":"date","config":{"formatter":"MM-DD-YYYY"}}]`, "invalid for date"},
|
||||
{"currency formatter", `[{"fieldName":"金额","type":"currency","config":{"formatter":"THOUSAND"}}]`, "invalid for currency"},
|
||||
{"currency type", `[{"fieldName":"金额","type":"currency","config":{"currencyType":"RMB"}}]`, "currencyType \"RMB\" is unsupported"},
|
||||
{"progress formatter", `[{"fieldName":"进度","type":"progress","config":{"formatter":"PERCENT_FLOAT"}}]`, "invalid for progress"},
|
||||
{"rating max", `[{"fieldName":"评分","type":"rating","config":{"max":11}}]`, "between 1 and 10"},
|
||||
{"multiple must be boolean", `[{"fieldName":"负责人","type":"user","config":{"multiple":"false"}}]`, "must be true or false"},
|
||||
{"formula missing formula", `[{"fieldName":"总价","type":"formula","config":{}}]`, "config.formula is required"},
|
||||
{"link missing table", `[{"fieldName":"关联","type":"unidirectionalLink","config":{"multiple":true}}]`, "linkedTableId is required"},
|
||||
{"primary doc not first", `[{"fieldName":"名称","type":"text"},{"fieldName":"文档","type":"primaryDoc"}]`, "only as --fields[0]"},
|
||||
}
|
||||
for _, test := range cases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
for _, args := range [][]string{
|
||||
{"table", "create", "--base-id=b", "--name=n", "--fields=" + test.fields},
|
||||
{"field", "create", "--base-id=b", "--table-id=t", "--fields=" + test.fields},
|
||||
} {
|
||||
caller := &aitableTestCaller{}
|
||||
err := runAitableCoverageCommand(t, caller, args...)
|
||||
if err == nil || !strings.Contains(err.Error(), test.hint) {
|
||||
t.Fatalf("invalid fields error = %v, want hint %q", err, test.hint)
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("invalid fields dispatched %d MCP call(s), want zero", len(caller.calls))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
args []string
|
||||
hint string
|
||||
}{
|
||||
{"typed unknown type", []string{"field", "create", "--base-id=b", "--table-id=t", "--name=字段", "--type=string"}, "type \"string\" is unsupported"},
|
||||
{"typed select alias", []string{"field", "create", "--base-id=b", "--table-id=t", "--name=状态", "--type=select"}, "singleSelect"},
|
||||
{"typed select no options", []string{"field", "create", "--base-id=b", "--table-id=t", "--name=状态", "--type=singleSelect"}, "config.options is required"},
|
||||
{"typed invalid formatter", []string{"field", "create", "--base-id=b", "--table-id=t", "--name=金额", "--type=number", `--config={"formatter":"CURRENCY_YUAN"}`}, "invalid for number"},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
caller := &aitableTestCaller{}
|
||||
err := runAitableCoverageCommand(t, caller, test.args...)
|
||||
if err == nil || !strings.Contains(err.Error(), test.hint) {
|
||||
t.Fatalf("typed field error = %v, want hint %q", err, test.hint)
|
||||
}
|
||||
if len(caller.calls) != 0 {
|
||||
t.Fatalf("typed invalid field dispatched %d MCP call(s), want zero", len(caller.calls))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageAitableCommandValidationEdges(t *testing.T) {
|
||||
oldDeps, oldArgs, oldStdin, oldSleep := deps, os.Args, os.Stdin, helperSleep
|
||||
t.Cleanup(func() {
|
||||
|
||||
@@ -180,7 +180,9 @@ func TestCrossPlatformCoverageAitableViewConfigAndHelpers(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
err error
|
||||
want bool
|
||||
}{{nil, false}, {errors.New("timeout"), true}, {errors.New("SYSTEM_ERROR"), true}, {errors.New("retryable: true"), true}, {errors.New("bad request"), false}} {
|
||||
}{{nil, false}, {errors.New("timeout"), true}, {errors.New("SYSTEM_ERROR"), true}, {errors.New("retryable: true"), true}, {errors.New("bad request"), false},
|
||||
{errors.New(`category: internal_error, type: INPUT_ERROR, retryable: true, message: no record`), false},
|
||||
{errors.New(`category: internal_error, retryable: true, message: resource not found`), false}} {
|
||||
if got := isAitableRetryableError(tc.err); got != tc.want {
|
||||
t.Errorf("isAitableRetryableError(%v) = %v", tc.err, got)
|
||||
}
|
||||
@@ -248,15 +250,26 @@ func TestCrossPlatformCoverageAitableViewConfigAndHelpers(t *testing.T) {
|
||||
t.Fatalf("typed-only update = %#v, %v", merged, err)
|
||||
}
|
||||
|
||||
for _, raw := range []string{`[1]`, `{"fields":[1]}`, `{}`, `{`} {
|
||||
for _, raw := range []string{`[{"fieldName":"N","type":"text"}]`, `{"fields":[{"fieldName":"N","type":"text"}]}`, `{}`, `{`} {
|
||||
fields, err := parseFieldsJSON(raw)
|
||||
if (raw == `[1]` || strings.Contains(raw, "fields")) && (err != nil || len(fields) != 1) {
|
||||
if (strings.HasPrefix(raw, `[{`) || strings.Contains(raw, "fields")) && (err != nil || len(fields) != 1) {
|
||||
t.Errorf("parseFieldsJSON(%q) = %#v, %v", raw, fields, err)
|
||||
}
|
||||
if (raw == `{}` || raw == `{`) && err == nil {
|
||||
t.Errorf("parseFieldsJSON(%q) should fail", raw)
|
||||
}
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
raw string
|
||||
hint string
|
||||
}{
|
||||
{`[{"fieldName":"状态","type":"select"}]`, "singleSelect"},
|
||||
{`[{"fieldName":"状态","fieldType":"singleSelect"}]`, "fieldName + type"},
|
||||
} {
|
||||
if _, err := parseFieldsJSON(tc.raw); err == nil || !strings.Contains(err.Error(), tc.hint) {
|
||||
t.Errorf("parseFieldsJSON(%q) error = %v, want hint %q", tc.raw, err, tc.hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageAitableToolResponseAndPaginationHelpers(t *testing.T) {
|
||||
@@ -312,4 +325,12 @@ func TestCrossPlatformCoverageAitableToolResponseAndPaginationHelpers(t *testing
|
||||
if err := recordQueryFetchAll(map[string]any{}, 1); err == nil {
|
||||
t.Fatal("first-page pagination error should fail")
|
||||
}
|
||||
caller = &aitableTestCaller{responses: []string{
|
||||
`{"data":{"records":[{"id":1}],"nextCursor":"same"}}`,
|
||||
`{"data":{"records":[{"id":2}],"nextCursor":"same"}}`,
|
||||
}}
|
||||
out = installAitableDeps(t, caller)
|
||||
if err := recordQueryFetchAll(map[string]any{}, 0); err != nil || !strings.Contains(out.String(), `"stopReason"`) || !strings.Contains(out.String(), `"cursor_not_advanced"`) {
|
||||
t.Fatalf("repeated cursor guard = %q, %v", out.String(), err)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-12
@@ -462,9 +462,12 @@ func guardGroupOwnerRemoval(ctx context.Context, openConversationID string, remo
|
||||
if err != nil || ownerOpenID == "" {
|
||||
return nil
|
||||
}
|
||||
ownerErr := fmt.Errorf(
|
||||
"refusing to remove the group owner: 被移除列表包含群主,移出群主将导致群无群主(孤儿群)\n hint: 先执行 dws chat group transfer-owner --group %s --user <newOwnerUserId> 转让群主后再移除",
|
||||
openConversationID,
|
||||
ownerErr := apperrors.NewValidation(
|
||||
"被移除列表包含群主,不能直接移出群主",
|
||||
apperrors.WithReason("group_owner_in_remove_list"),
|
||||
apperrors.WithHint("清理临时群成员时应从 --users 中移除群主,只移除本次加入的普通成员;只有用户明确要求变更群主时才单独执行 transfer-owner。"),
|
||||
apperrors.WithActions("从 --users 中移除群主后重试", "若用户明确要求转让群主,先确认新群主再单独执行 transfer-owner"),
|
||||
apperrors.WithExamples(fmt.Sprintf("dws chat group members remove --id %s --users <普通成员userId列表> --format json", openConversationID)),
|
||||
)
|
||||
userIDs, openDingTalkIDs := splitChatIDValues(removeValues)
|
||||
for _, id := range openDingTalkIDs {
|
||||
@@ -2238,7 +2241,7 @@ func newChatCommand() *cobra.Command {
|
||||
hasCondition := false
|
||||
for _, name := range []string{
|
||||
"query", "keyword", "user", "users", "userId", "sender-ids", "senders", "sender",
|
||||
"at-ids", "conversation-ids", "groups", "group", "message-type",
|
||||
"at-ids", "conversation-ids", "conversation-id", "groups", "group", "message-type",
|
||||
"conversation-type", "search-conv-type", "start", "end",
|
||||
} {
|
||||
if value, _ := cmd.Flags().GetString(name); strings.TrimSpace(value) != "" {
|
||||
@@ -2291,6 +2294,8 @@ func newChatCommand() *cobra.Command {
|
||||
convIds := ""
|
||||
if v, _ := cmd.Flags().GetString("conversation-ids"); v != "" {
|
||||
convIds = v
|
||||
} else if v, _ := cmd.Flags().GetString("conversation-id"); v != "" {
|
||||
convIds = v
|
||||
} else if v, _ := cmd.Flags().GetString("groups"); v != "" {
|
||||
convIds = v
|
||||
} else if v, _ := cmd.Flags().GetString("group"); v != "" {
|
||||
@@ -2581,7 +2586,6 @@ func newChatCommand() *cobra.Command {
|
||||
_ = chatGroupMemberRemoveCmd.MarkFlagRequired("users")
|
||||
|
||||
chatGroupCmd.AddCommand(chatGroupCreateCmd, chatGroupMembersCmd, chatGroupRenameCmd)
|
||||
chatGroupCmd.AddCommand(hintSubCmd("search", "use: dws chat search --query <关键词>"))
|
||||
chatGroupMembersCmd.AddCommand(chatGroupMemberAddCmd, chatGroupMemberRemoveCmd, chatGroupMembersAddBotCmd)
|
||||
|
||||
// message 子命令 flags
|
||||
@@ -2816,6 +2820,8 @@ func newChatCommand() *cobra.Command {
|
||||
chatMessageSearchAdvancedCmd.Flags().Bool("at-me", false, "只搜索 @我 的消息(可选,默认 false)")
|
||||
chatMessageSearchAdvancedCmd.Flags().String("at-ids", "", "@指定人的 openDingTalkId 列表,逗号分隔(可选)")
|
||||
chatMessageSearchAdvancedCmd.Flags().String("conversation-ids", "", "会话 openConversationId 列表,逗号分隔(可选,群聊或单聊均可,不传则搜索所有会话)")
|
||||
chatMessageSearchAdvancedCmd.Flags().String("conversation-id", "", "--conversation-ids 的单值兼容别名")
|
||||
_ = chatMessageSearchAdvancedCmd.Flags().MarkHidden("conversation-id")
|
||||
chatMessageSearchAdvancedCmd.Flags().String("groups", "", "--conversation-ids 的别名")
|
||||
_ = chatMessageSearchAdvancedCmd.Flags().MarkHidden("groups")
|
||||
chatMessageSearchAdvancedCmd.Flags().String("group", "", "")
|
||||
@@ -3770,8 +3776,8 @@ flow-status 取值:1=处理中(PROCESSING),2=输入中(INPUTTING),3=完成
|
||||
return err
|
||||
}
|
||||
iconMediaID := strings.TrimSpace(mustGetFlag(cmd, "icon-media-id"))
|
||||
if iconMediaID == "" {
|
||||
return fmt.Errorf("invalid --icon-media-id: mediaId 不能为空\n hint: 请使用上游媒体上传能力返回的有效 mediaId;DWS CLI 不提供本地文件到 mediaId 的上传命令")
|
||||
if err := ValidateChatMediaID(iconMediaID); err != nil {
|
||||
return fmt.Errorf("invalid --icon-media-id: %w", err)
|
||||
}
|
||||
return callMCPToolOnServer("im", "update_group_icon", map[string]any{
|
||||
"openConversationId": mustGetFlag(cmd, "group"),
|
||||
@@ -5736,10 +5742,5 @@ pl_PL, sv_SE, fi_FI, cs_CZ, ar_SA, tl_PH, he_IL, nl_NL, lo_LA, it_IT`,
|
||||
|
||||
root.AddCommand(chatChmodCmd, chatDataAuthCmd, chatGroupCmd, chatSearchCmd, chatSearchCommonCmd, chatMessageCmd, chatFileCmd, newChatMediaGroup(), chatBotCmd, chatMessageListTopConversationsCmd, chatConversationInfoCmd, chatCategoryCmd, chatGroupRoleCmd, chatMuteCmd, chatSetTopCmd, chatGroupMuteCmd, chatGroupMuteMemberCmd, chatHideCmd, chatMuteAtAllCmd, chatMuteRedEnvelopeCmd, chatMarkUnreadCmd, chatClearRedPointCmd, chatClearAllRedPointCmd, chatListAllConversationsCmd, chatClearMessagesCmd, chatMarkReadCmd, chatTextCmd)
|
||||
|
||||
// hint: dws chat send → dws chat message send
|
||||
root.AddCommand(hintSubCmd("send", "use: dws chat message send"))
|
||||
// hint: dws chat history → dws chat message list
|
||||
root.AddCommand(hintSubCmd("history", "use: dws chat message list --group <GROUP_OPEN_CONVERSATION_ID>"))
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
@@ -85,6 +85,22 @@ func TestCrossPlatformCoverageChatGroupUpdateIconRejectsBlankMediaID(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageChatGroupUpdateIconRejectsLocalPathBeforeMCP(t *testing.T) {
|
||||
previousDeps, previousArgs := deps, os.Args
|
||||
os.Args = []string{"dws", "chat"}
|
||||
t.Cleanup(func() { deps, os.Args = previousDeps, previousArgs })
|
||||
|
||||
caller := &productExampleCaller{}
|
||||
err := runChatCoverageCommand(t, caller,
|
||||
"group", "update-icon", "--group=cid", "--icon-media-id=./logo.png")
|
||||
if err == nil {
|
||||
t.Fatal("update group icon with local path succeeded, want validation error")
|
||||
}
|
||||
if caller.calls != 0 {
|
||||
t.Fatalf("tool calls = %d, want 0", caller.calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageChatCommandValidationAndSuccessEdges(t *testing.T) {
|
||||
previousDeps, previousArgs := deps, os.Args
|
||||
os.Args = []string{"dws", "chat"}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -406,7 +407,13 @@ func TestCrossPlatformCoverageGuardGroupOwnerRemovalCoverage(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
caller := &scriptedToolCaller{steps: tc.steps}
|
||||
installScriptedCaller(t, caller)
|
||||
_ = guardGroupOwnerRemoval(context.Background(), "group", tc.remove)
|
||||
err := guardGroupOwnerRemoval(context.Background(), "group", tc.remove)
|
||||
if tc.name == "owner-open" {
|
||||
var typed *apperrors.Error
|
||||
if !errors.As(err, &typed) || typed.Reason != "group_owner_in_remove_list" || !strings.Contains(typed.Hint, "从 --users 中移除群主") || strings.Contains(typed.Hint, "先执行 dws chat group transfer-owner") {
|
||||
t.Fatalf("owner removal hint = %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
caller := &scriptedToolCaller{steps: []scriptedToolStep{{text: `{}`}}}
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
|
||||
package helpers
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ConversationLocalFileMeta exposes the already-reviewed native chat upload
|
||||
// metadata to built-in semantic Shortcuts. It remains an alias so the native
|
||||
@@ -41,3 +46,21 @@ func BuildConversationFileContent(
|
||||
) (string, error) {
|
||||
return buildConversationFileContent(dentryID, spaceID, meta)
|
||||
}
|
||||
|
||||
// ValidateChatMediaID rejects values that are deterministically not an
|
||||
// uploaded DingTalk mediaId before an MCP request is dispatched.
|
||||
func ValidateChatMediaID(value string) error {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return fmt.Errorf("mediaId 不能为空")
|
||||
}
|
||||
lower := strings.ToLower(value)
|
||||
if strings.HasPrefix(lower, "file://") || filepath.IsAbs(value) ||
|
||||
strings.ContainsAny(value, `/\\`) || strings.HasPrefix(lower, "dentry") {
|
||||
return fmt.Errorf("%q 是本地文件路径或文件标识,不是 mediaId;请使用可信上游返回的 mediaId,DWS CLI 当前不能把本地图片转换为群头像 mediaId", value)
|
||||
}
|
||||
if value[0] != '@' && value[0] != '$' {
|
||||
return fmt.Errorf("%q 不是有效 mediaId:群头像 mediaId 应以 @ 或 $ 开头;不要传本地路径、dentryId 或 uploadKey", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+25
-52
@@ -33,6 +33,27 @@ func SetHTTPPutFile(fn func(ctx context.Context, url string, headers map[string]
|
||||
httpPutFile = fn
|
||||
}
|
||||
|
||||
func callDocCommentUpdate(toolArgs map[string]any) error {
|
||||
text, err := callMCPToolReturnTextOnServer(context.Background(), "doc-comment", "update_comment", toolArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trimmed := strings.TrimSpace(text)
|
||||
if trimmed == "" || trimmed == "null" {
|
||||
return &CLIError{
|
||||
Code: CodeMCPToolError,
|
||||
Message: "评论更新接口未返回可验证的更新结果,不能判定为成功",
|
||||
Suggestion: "请用 dws doc comment list --node <DOC_ID> --format json 回查评论内容;若未变化,保留原 commentKey 并重试",
|
||||
}
|
||||
}
|
||||
var payload any
|
||||
if json.Unmarshal([]byte(trimmed), &payload) == nil {
|
||||
return deps.Out.PrintJSON(payload)
|
||||
}
|
||||
deps.Out.PrintRaw(text)
|
||||
return nil
|
||||
}
|
||||
|
||||
func docVersionExists(ctx context.Context, nodeID string, version int) (bool, error) {
|
||||
// 注意: 不传 maxResults —— 服务端实际接受的上限小于 schema 声明的 1-50,
|
||||
// 传大值会直接报错 (与悟空实现一致: 默认分页大小 + 游标翻页)。
|
||||
@@ -1070,9 +1091,6 @@ func newDocCommand() *cobra.Command {
|
||||
})
|
||||
}
|
||||
if md != "" {
|
||||
if name, ok := toolArgs["name"].(string); ok && name != "" {
|
||||
md = stripDuplicateTitle(md, name)
|
||||
}
|
||||
toolArgs["markdown"] = md
|
||||
}
|
||||
if md != "" {
|
||||
@@ -1652,7 +1670,8 @@ WARNING: --mode overwrite 为破坏性写入,会清空原文档全部内容。
|
||||
updateCmd.Flags().String("markdown", "", "已弃用,请使用 --content 代替")
|
||||
_ = updateCmd.Flags().MarkHidden("markdown")
|
||||
updateCmd.Flags().String("mode", "", "更新模式: overwrite=覆盖, append=追加 (必填)")
|
||||
_ = updateCmd.MarkFlagRequired("mode")
|
||||
// Kept out of Cobra's generic required-flag validator so doc local
|
||||
// preflight can return a structured, actionable error before MCP dispatch.
|
||||
updateCmd.Flags().Int("index", -1, "插入位置(从 0 开始),仅在 mode=append 时生效。指定将内容插入到文档第几个 block 之前。不传时追加到末尾")
|
||||
updateCmd.Flags().Bool("yes", false, "确认执行破坏性写入 (仅 --mode overwrite 需要)")
|
||||
updateCmd.Flags().Bool("dry-run", false, "预览覆盖写入差异,不调用远端 update")
|
||||
@@ -2046,7 +2065,7 @@ commentKey可从 dws doc comment create 或 dws doc comment list 返回结果中
|
||||
if err := appendCommentGroupMentions(cmd, toolArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
return callMCPToolOnServer("doc-comment", "update_comment", toolArgs)
|
||||
return callDocCommentUpdate(toolArgs)
|
||||
},
|
||||
}
|
||||
commentUpdateCmd.Flags().String("node", "", "目标文档的标识,支持传入 URL 或 ID (必填)")
|
||||
@@ -2902,6 +2921,7 @@ CLI 内部自动完成全部流程:
|
||||
permissionCmd.Hidden = true
|
||||
|
||||
root.AddCommand(searchCmd, listCmd, infoCmd, readCmd, createCmd, updateCmd, uploadCmd, downloadCmd, copyCmd, moveCmd, renameCmd, deleteCmd, fileCmd, folderCmd, blockCmd, commentCmd, mediaCmd, permissionCmd, exportCmd, importCmd, versionCmd, templateCmd, newDocStyleCommand())
|
||||
attachDocLocalPreflight(root)
|
||||
|
||||
return root
|
||||
}
|
||||
@@ -3218,53 +3238,6 @@ func pollDocExportJob(ctx context.Context, jobID string) (downloadURL string, er
|
||||
return "", fmt.Errorf("导出任务超时:已轮询 %d 次仍在处理中 (jobId=%s),请稍后使用 dws doc export get --job-id %s 手动查询", maxPolls, jobID, jobID)
|
||||
}
|
||||
|
||||
// stripDuplicateTitle removes the leading H1 heading from markdown content
|
||||
// when it matches the document name (set via --name). This prevents the title
|
||||
// from appearing twice: once as document metadata and once in the body.
|
||||
func stripDuplicateTitle(markdown, name string) string {
|
||||
trimmed := strings.TrimLeft(markdown, " \t\n\r")
|
||||
if !strings.HasPrefix(trimmed, "# ") {
|
||||
return markdown
|
||||
}
|
||||
newlineIdx := strings.Index(trimmed, "\n")
|
||||
var headingRaw string
|
||||
if newlineIdx < 0 {
|
||||
headingRaw = trimmed[2:]
|
||||
} else {
|
||||
headingRaw = trimmed[2:newlineIdx]
|
||||
}
|
||||
|
||||
if normalizeHeadingText(headingRaw) != normalizeHeadingText(name) {
|
||||
return markdown
|
||||
}
|
||||
|
||||
if newlineIdx < 0 {
|
||||
return ""
|
||||
}
|
||||
rest := trimmed[newlineIdx+1:]
|
||||
rest = strings.TrimLeft(rest, "\n")
|
||||
return rest
|
||||
}
|
||||
|
||||
// normalizeHeadingText strips trailing ATX hashes, inline markdown formatting
|
||||
// markers, then returns a lowercased, trimmed string for comparison.
|
||||
func normalizeHeadingText(s string) string {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
if i := strings.LastIndexByte(s, ' '); i >= 0 {
|
||||
suffix := s[i+1:]
|
||||
if len(suffix) > 0 && strings.Trim(suffix, "#") == "" {
|
||||
s = strings.TrimSpace(s[:i])
|
||||
}
|
||||
}
|
||||
for _, m := range []string{"**", "__", "~~", "*", "_", "`"} {
|
||||
s = strings.ReplaceAll(s, m, "")
|
||||
}
|
||||
return strings.TrimSpace(strings.ToLower(s))
|
||||
}
|
||||
|
||||
// parseCommentMentionIds splits a comma-separated string of user IDs into a slice.
|
||||
func parseCommentMentionIds(raw string) []string {
|
||||
parts := strings.Split(raw, ",")
|
||||
|
||||
@@ -295,12 +295,6 @@ func TestCrossPlatformCoverageDocCreateUpdateAndBlockCommandEdges(t *testing.T)
|
||||
})
|
||||
}
|
||||
|
||||
for _, value := range []string{"plain", "# Other\nbody", "# Name", "# **Name** ###\n\nbody"} {
|
||||
_ = stripDuplicateTitle(value, "Name")
|
||||
}
|
||||
for _, value := range []string{"", " Name ### ", "**Bold**", "__Under__ ~~Strike~~ `Code`"} {
|
||||
_ = normalizeHeadingText(value)
|
||||
}
|
||||
for _, name := range []string{"file.pdf", "file.md", "file.unknown"} {
|
||||
_ = inferMimeType(name)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
@@ -30,12 +31,29 @@ type docCommentMutationCall struct {
|
||||
}
|
||||
|
||||
type docCommentMutationCaller struct {
|
||||
calls []docCommentMutationCall
|
||||
calls []docCommentMutationCall
|
||||
response string
|
||||
}
|
||||
|
||||
func (c *docCommentMutationCaller) CallTool(_ context.Context, productID, toolName string, args map[string]any) (*edition.ToolResult, error) {
|
||||
c.calls = append(c.calls, docCommentMutationCall{productID: productID, toolName: toolName, args: args})
|
||||
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: `{}`}}}, nil
|
||||
response := c.response
|
||||
if response == "" {
|
||||
response = `{}`
|
||||
}
|
||||
return &edition.ToolResult{Content: []edition.ContentBlock{{Type: "text", Text: response}}}, nil
|
||||
}
|
||||
|
||||
func TestDocCommentUpdateRejectsNullAcknowledgement(t *testing.T) {
|
||||
caller := &docCommentMutationCaller{response: `null`}
|
||||
err := executeDocCommentMutationCommand(t, caller, []string{"dws", "doc"},
|
||||
"comment", "update", "--node", "doc-1", "--comment-key", "comment-1", "--content", "updated")
|
||||
if err == nil || !strings.Contains(err.Error(), "未返回可验证的更新结果") {
|
||||
t.Fatalf("error = %v, want unverifiable update error", err)
|
||||
}
|
||||
if len(caller.calls) != 1 {
|
||||
t.Fatalf("remote calls = %d, want 1", len(caller.calls))
|
||||
}
|
||||
}
|
||||
|
||||
func (*docCommentMutationCaller) Format() string { return "json" }
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/pkg/edition"
|
||||
)
|
||||
|
||||
type docCreateRecordingCall struct {
|
||||
tool string
|
||||
args map[string]any
|
||||
}
|
||||
|
||||
type docCreateRecordingCaller struct {
|
||||
calls []docCreateRecordingCall
|
||||
}
|
||||
|
||||
func (c *docCreateRecordingCaller) CallTool(_ context.Context, _ string, tool string, args map[string]any) (*edition.ToolResult, error) {
|
||||
copied := make(map[string]any, len(args))
|
||||
for key, value := range args {
|
||||
copied[key] = value
|
||||
}
|
||||
c.calls = append(c.calls, docCreateRecordingCall{tool: tool, args: copied})
|
||||
return textToolResult(`{"nodeId":"node-1","success":true}`), nil
|
||||
}
|
||||
|
||||
func (*docCreateRecordingCaller) Format() string { return "json" }
|
||||
func (*docCreateRecordingCaller) DryRun() bool { return false }
|
||||
func (*docCreateRecordingCaller) Fields() string { return "" }
|
||||
func (*docCreateRecordingCaller) JQ() string { return "" }
|
||||
|
||||
func TestDocCreatePreservesExplicitLeadingH1MatchingName(t *testing.T) {
|
||||
oldArgs := os.Args
|
||||
os.Args = []string{"dws", "doc"}
|
||||
t.Cleanup(func() { os.Args = oldArgs })
|
||||
|
||||
for _, content := range []string{
|
||||
"# 需求清单",
|
||||
"# 需求清单\n\n以上需求已与产品确认",
|
||||
} {
|
||||
t.Run(content, func(t *testing.T) {
|
||||
previous := deps
|
||||
caller := &docCreateRecordingCaller{}
|
||||
InitDeps(caller)
|
||||
deps.Out.w = io.Discard
|
||||
deps.Out.errW = io.Discard
|
||||
t.Cleanup(func() { deps = previous })
|
||||
|
||||
root := newDocCommand()
|
||||
root.SilenceErrors = true
|
||||
root.SilenceUsage = true
|
||||
root.SetOut(io.Discard)
|
||||
root.SetErr(io.Discard)
|
||||
root.SetArgs([]string{"create", "--name", "需求清单", "--content", content})
|
||||
|
||||
if err := root.ExecuteContext(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(caller.calls) != 1 {
|
||||
t.Fatalf("tool calls = %#v, want one create_document call", caller.calls)
|
||||
}
|
||||
call := caller.calls[0]
|
||||
if call.tool != "create_document" {
|
||||
t.Fatalf("tool = %q, want create_document", call.tool)
|
||||
}
|
||||
if got := call.args["markdown"]; got != content {
|
||||
t.Fatalf("markdown = %#v, want exact explicit body H1 %#v", got, content)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,446 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// attachDocLocalPreflight wraps every doc leaf before its RunE reaches MCP.
|
||||
// Only facts provable from local argv/filesystem state belong here; resource
|
||||
// existence, permissions and business state remain server-owned.
|
||||
func attachDocLocalPreflight(root *cobra.Command) {
|
||||
var walk func(*cobra.Command)
|
||||
walk = func(cmd *cobra.Command) {
|
||||
for _, child := range cmd.Commands() {
|
||||
walk(child)
|
||||
}
|
||||
if cmd.RunE == nil {
|
||||
return
|
||||
}
|
||||
original := cmd.RunE
|
||||
cmd.RunE = func(c *cobra.Command, args []string) error {
|
||||
if err := validateDocLocalArgs(c); err != nil {
|
||||
return err
|
||||
}
|
||||
return original(c, args)
|
||||
}
|
||||
}
|
||||
walk(root)
|
||||
}
|
||||
|
||||
func docLocalError(cmd *cobra.Command, code, message, suggestion string) error {
|
||||
return &CLIError{Code: code, Message: message, Suggestion: suggestion, Operation: strings.TrimPrefix(cmd.CommandPath(), "dws ")}
|
||||
}
|
||||
|
||||
func docRequire(cmd *cobra.Command, example string, names ...string) error {
|
||||
missing := make([]string, 0, len(names))
|
||||
for _, name := range names {
|
||||
flag := cmd.Flags().Lookup(name)
|
||||
if flag == nil || strings.TrimSpace(flag.Value.String()) == "" {
|
||||
missing = append(missing, "--"+name)
|
||||
}
|
||||
}
|
||||
if len(missing) == 0 {
|
||||
return nil
|
||||
}
|
||||
return docLocalError(cmd, CodeMissingParam, "缺少必填参数: "+strings.Join(missing, ", "), "示例: "+example)
|
||||
}
|
||||
|
||||
func docRequireNode(cmd *cobra.Command, example string) error {
|
||||
if flagOrFallback(cmd, "node", "url", "id", "node-id", "doc-id", "file-id") != "" {
|
||||
return nil
|
||||
}
|
||||
return docLocalError(cmd, CodeMissingParam, "缺少目标文档 --node(可传文档 URL 或 nodeId)", "先用 dws drive search 或 dws wiki node search 获取 nodeId。示例: "+example)
|
||||
}
|
||||
|
||||
func docValidateLocalFile(cmd *cobra.Command, flagName, example string) error {
|
||||
path, _ := cmd.Flags().GetString(flagName)
|
||||
if strings.TrimSpace(path) == "" {
|
||||
return docRequire(cmd, example, flagName)
|
||||
}
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return docLocalError(cmd, CodeFileNotFound, fmt.Sprintf("本地文件 %q 不可读取", path), "检查路径后重试。示例: "+example)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return docLocalError(cmd, CodeInvalidPath, fmt.Sprintf("%q 是目录,不是文件", path), "请传入具体文件路径。示例: "+example)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func docValidateWhere(cmd *cobra.Command, example string) error {
|
||||
where, _ := cmd.Flags().GetString("where")
|
||||
ref, _ := cmd.Flags().GetString("ref-block")
|
||||
if where != "" && where != "before" && where != "after" {
|
||||
return docLocalError(cmd, CodeInvalidParam, fmt.Sprintf("--where %q 无效,仅支持 before 或 after", where), "示例: "+example)
|
||||
}
|
||||
if where != "" && ref == "" {
|
||||
return docLocalError(cmd, CodeMissingParam, "使用 --where 时必须同时提供 --ref-block", "先用 dws doc block list --node <DOC_ID> 获取 blockId。示例: "+example)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func docValidateEnum(cmd *cobra.Command, name string, allowed []string, example string) error {
|
||||
value, _ := cmd.Flags().GetString(name)
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
for _, candidate := range allowed {
|
||||
if strings.EqualFold(value, candidate) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return docLocalError(cmd, CodeInvalidParam, fmt.Sprintf("--%s %q 无效,仅支持 %s", name, value, strings.Join(allowed, "、")), "示例: "+example)
|
||||
}
|
||||
|
||||
func docValidateLimit(cmd *cobra.Command, max int, example string) error {
|
||||
for _, name := range []string{"limit", "page-size", "max-results"} {
|
||||
if flag := cmd.Flags().Lookup(name); flag != nil && cmd.Flags().Changed(name) {
|
||||
value, err := cmd.Flags().GetInt(name)
|
||||
if err == nil && (value < 1 || value > max) {
|
||||
return docLocalError(cmd, CodeInvalidParam, fmt.Sprintf("--%s 必须在 1 到 %d 之间", name, max), "示例: "+example)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func docValidateBlockContent(cmd *cobra.Command, example string) error {
|
||||
changed := make([]string, 0, 3)
|
||||
for _, name := range []string{"text", "heading", "element"} {
|
||||
if flag := cmd.Flags().Lookup(name); flag != nil && cmd.Flags().Changed(name) && strings.TrimSpace(flag.Value.String()) != "" {
|
||||
changed = append(changed, "--"+name)
|
||||
}
|
||||
}
|
||||
if len(changed) == 0 {
|
||||
return docLocalError(cmd, CodeMissingParam, "必须提供一种块内容:--text、--heading 或 --element", "示例: "+example)
|
||||
}
|
||||
if len(changed) > 1 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "块内容参数不能同时使用: "+strings.Join(changed, ", "), "三选一。示例: "+example)
|
||||
}
|
||||
format, _ := cmd.Flags().GetString("content-format")
|
||||
if format == "jsonml" && changed[0] != "--element" {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--content-format jsonml 必须通过 --element 提供 JSONML 节点", "示例: "+example)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func docValidatePermissionUsers(cmd *cobra.Command, example string) error {
|
||||
raw := flagOrFallback(cmd, "users", "user")
|
||||
users := parseCommentMentionIds(raw)
|
||||
if len(users) == 0 {
|
||||
return docLocalError(cmd, CodeMissingParam, "缺少至少一个用户 ID:--users", "示例: "+example)
|
||||
}
|
||||
if len(users) > 30 {
|
||||
return docLocalError(cmd, CodeInvalidParam, fmt.Sprintf("单次最多处理 30 个用户,当前为 %d 个", len(users)), "请拆分为多次调用")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDocLocalArgs(cmd *cobra.Command) error {
|
||||
path := strings.TrimPrefix(cmd.CommandPath(), "dws ")
|
||||
switch path {
|
||||
case "doc info":
|
||||
return docRequireNode(cmd, "dws "+path+" --node <DOC_ID> --format json")
|
||||
case "doc search":
|
||||
if err := docValidateLimit(cmd, 30, "dws doc search --query \"周报\" --limit 10"); err != nil {
|
||||
return err
|
||||
}
|
||||
createdFrom, _ := cmd.Flags().GetInt64("created-from")
|
||||
createdTo, _ := cmd.Flags().GetInt64("created-to")
|
||||
visitedFrom, _ := cmd.Flags().GetInt64("visited-from")
|
||||
visitedTo, _ := cmd.Flags().GetInt64("visited-to")
|
||||
if createdFrom < 0 || createdTo < 0 || visitedFrom < 0 || visitedTo < 0 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "时间过滤值必须是非负毫秒时间戳", "示例: dws doc search --created-from 1700000000000 --created-to 1710000000000")
|
||||
}
|
||||
if cmd.Flags().Changed("created-from") && cmd.Flags().Changed("created-to") && createdFrom > createdTo {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--created-from 不能晚于 --created-to", "请交换起止时间")
|
||||
}
|
||||
if cmd.Flags().Changed("visited-from") && cmd.Flags().Changed("visited-to") && visitedFrom > visitedTo {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--visited-from 不能晚于 --visited-to", "请交换起止时间")
|
||||
}
|
||||
case "doc list":
|
||||
return docValidateLimit(cmd, 50, "dws doc list --workspace <WORKSPACE_ID> --limit 50")
|
||||
case "doc read":
|
||||
if err := docRequireNode(cmd, "dws doc read --node <DOC_ID> --format json"); err != nil {
|
||||
return err
|
||||
}
|
||||
if depth, _ := cmd.Flags().GetInt("max-depth"); depth < 0 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--max-depth 不能为负数", "示例: dws doc read --node <DOC_ID> --content-format jsonml --scope outline --max-depth 3")
|
||||
}
|
||||
if scopeValue, _ := cmd.Flags().GetString("scope"); scopeValue != "" {
|
||||
valid := false
|
||||
for _, candidate := range []string{"outline", "range", "section", "tags"} {
|
||||
if scopeValue == candidate {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return docLocalError(cmd, CodeInvalidParam, fmt.Sprintf("invalid --scope %q: must be one of outline|range|section|tags", scopeValue), "示例: dws doc read --node <DOC_ID> --content-format jsonml --scope outline")
|
||||
}
|
||||
}
|
||||
format, _ := cmd.Flags().GetString("content-format")
|
||||
scope, _ := cmd.Flags().GetString("scope")
|
||||
tags, _ := cmd.Flags().GetString("tags")
|
||||
startBlockID, _ := cmd.Flags().GetString("start-block-id")
|
||||
if (scope == "range" || scope == "section") && strings.TrimSpace(startBlockID) == "" {
|
||||
return docLocalError(cmd, CodeMissingParam, "--scope "+scope+" 必须提供 --start-block-id", "先用 dws doc block list --node <DOC_ID> --format json 获取真实 blockId")
|
||||
}
|
||||
if cmd.Flags().Changed("output") && format != "jsonml" {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--output 仅支持 --content-format jsonml", "Markdown 内容会直接显示在终端;如需保存为文件,请执行: dws doc read --node <DOC_ID> --content-format markdown --format raw > body.md")
|
||||
}
|
||||
if (scope != "" || tags != "") && format != "jsonml" {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--scope/--tags requires --content-format jsonml", "示例: dws doc read --node <DOC_ID> --content-format jsonml --scope tags --tags h1,h2")
|
||||
}
|
||||
if scope == "tags" && strings.TrimSpace(tags) == "" {
|
||||
return docLocalError(cmd, CodeMissingParam, "--tags is required when --scope=tags", "示例: dws doc read --node <DOC_ID> --content-format jsonml --scope tags --tags h1,h2")
|
||||
}
|
||||
if scope != "tags" && tags != "" {
|
||||
if scope == "" {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--tags requires --scope tags", "移除 --tags 或改用 --scope tags")
|
||||
}
|
||||
return docLocalError(cmd, CodeInvalidParam, "--tags only works with --scope tags", "移除 --tags 或改用 --scope tags")
|
||||
}
|
||||
case "doc create":
|
||||
if strings.TrimSpace(flagOrFallback(cmd, "name", "title")) == "" {
|
||||
return docLocalError(cmd, CodeMissingParam, "缺少文档名称 --name", "示例: dws doc create --name \"项目周报\" --format json")
|
||||
}
|
||||
if cmd.Flags().Changed("content") && cmd.Flags().Changed("content-file") {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--content 与 --content-file 不能同时使用", "短文本用 --content;长文本或表格用 --content-file")
|
||||
}
|
||||
if cmd.Flags().Changed("content-file") {
|
||||
return docValidateLocalFile(cmd, "content-file", "dws doc create --name \"周报\" --content-file ./weekly.md")
|
||||
}
|
||||
case "doc update":
|
||||
if err := docRequireNode(cmd, "dws doc update --node <DOC_ID> --content \"追加内容\" --mode append"); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.Flags().Changed("content") && cmd.Flags().Changed("content-file") {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--content 与 --content-file 不能同时使用", "二选一;长内容优先 --content-file")
|
||||
}
|
||||
if cmd.Flags().Changed("content-file") {
|
||||
if err := docValidateLocalFile(cmd, "content-file", "dws doc update --node <DOC_ID> --content-file ./body.md --mode append"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
mode, _ := cmd.Flags().GetString("mode")
|
||||
if mode != "append" && mode != "overwrite" {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--mode 必须是 append 或 overwrite", "追加优先使用: dws doc update --node <DOC_ID> --content \"内容\" --mode append")
|
||||
}
|
||||
if idx, _ := cmd.Flags().GetInt("index"); cmd.Flags().Changed("index") && (mode != "append" || idx < 0) {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--index 仅支持 mode=append 且必须大于等于 0", "示例: dws doc update --node <DOC_ID> --content \"内容\" --mode append --index 0")
|
||||
}
|
||||
content := flagOrFallback(cmd, "content", "markdown")
|
||||
if strings.TrimSpace(content) == "" && !cmd.Flags().Changed("content-file") {
|
||||
return docLocalError(cmd, CodeMissingParam, "必须通过 --content 或 --content-file 提供非空内容", "示例: dws doc update --node <DOC_ID> --content \"追加内容\" --mode append")
|
||||
}
|
||||
dryRun, _ := cmd.Flags().GetBool("dry-run")
|
||||
yes, _ := cmd.Flags().GetBool("yes")
|
||||
if dryRun && mode != "overwrite" {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--dry-run 仅用于预览 overwrite 覆盖写入", "append 本身不覆盖全文,请移除 --dry-run")
|
||||
}
|
||||
if yes && mode != "overwrite" {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--yes 仅用于确认 overwrite 覆盖写入", "append 无需 --yes,请移除该参数")
|
||||
}
|
||||
if mode == "overwrite" && !dryRun && !yes {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--mode overwrite 必须加 --yes 确认,或使用 --dry-run 预览", "示例: dws doc update --node <DOC_ID> --content-file ./body.md --mode overwrite --dry-run")
|
||||
}
|
||||
case "doc block list":
|
||||
if err := docRequireNode(cmd, "dws doc block list --node <DOC_ID> --format json"); err != nil {
|
||||
return err
|
||||
}
|
||||
start, _ := cmd.Flags().GetInt("start-index")
|
||||
end, _ := cmd.Flags().GetInt("end-index")
|
||||
if start < 0 || end < 0 || (cmd.Flags().Changed("end-index") && end < start) {
|
||||
return docLocalError(cmd, CodeInvalidParam, "块索引必须非负,且 --end-index 不能小于 --start-index", "示例: dws doc block list --node <DOC_ID> --start-index 0 --end-index 5")
|
||||
}
|
||||
case "doc block insert":
|
||||
if err := docRequireNode(cmd, "dws doc block insert --node <DOC_ID> --text \"内容\""); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docValidateWhere(cmd, "dws doc block insert --node <DOC_ID> --text \"内容\" --ref-block <BLOCK_ID> --where after"); err != nil {
|
||||
return err
|
||||
}
|
||||
if level, _ := cmd.Flags().GetInt("level"); level < 1 || level > 6 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--level 必须在 1 到 6 之间", "示例: dws doc block insert --node <DOC_ID> --heading \"标题\" --level 2")
|
||||
}
|
||||
if idx, _ := cmd.Flags().GetInt("index"); cmd.Flags().Changed("index") && idx < 0 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--index 不能为负数", "示例: dws doc block insert --node <DOC_ID> --text \"内容\" --index 0")
|
||||
}
|
||||
return docValidateBlockContent(cmd, "dws doc block insert --node <DOC_ID> --text \"内容\"")
|
||||
case "doc block update":
|
||||
if err := docRequireNode(cmd, "dws doc block update --node <DOC_ID> --block-id <BLOCK_ID> --text \"新内容\""); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docRequire(cmd, "dws doc block update --node <DOC_ID> --block-id <BLOCK_ID> --text \"新内容\"", "block-id"); err != nil {
|
||||
return err
|
||||
}
|
||||
if level, _ := cmd.Flags().GetInt("level"); level < 1 || level > 6 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--level 必须在 1 到 6 之间", "示例: dws doc block update --node <DOC_ID> --block-id <BLOCK_ID> --heading \"标题\" --level 2")
|
||||
}
|
||||
return docValidateBlockContent(cmd, "dws doc block update --node <DOC_ID> --block-id <BLOCK_ID> --text \"新内容\"")
|
||||
case "doc block delete":
|
||||
if err := docRequireNode(cmd, "dws doc block delete --node <DOC_ID> --block-id <BLOCK_ID> --yes"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docRequire(cmd, "dws doc block delete --node <DOC_ID> --block-id <BLOCK_ID> --yes", "block-id")
|
||||
case "doc media download":
|
||||
if err := docRequireNode(cmd, "dws doc media download --node <DOC_ID> --resource-id <RESOURCE_ID>"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docRequire(cmd, "dws doc media download --node <DOC_ID> --resource-id <RESOURCE_ID>", "resource-id")
|
||||
case "doc media insert":
|
||||
if err := docRequireNode(cmd, "dws doc media insert --node <DOC_ID> --file ./report.pdf"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docValidateWhere(cmd, "dws doc media insert --node <DOC_ID> --file ./report.pdf --ref-block <BLOCK_ID> --where after"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docValidateLocalFile(cmd, "file", "dws doc media insert --node <DOC_ID> --file ./report.pdf")
|
||||
case "doc comment list":
|
||||
if err := docRequireNode(cmd, "dws doc comment list --node <DOC_ID> --format json"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docValidateLimit(cmd, 50, "dws doc comment list --node <DOC_ID> --limit 50"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docValidateEnum(cmd, "type", []string{"global", "inline"}, "dws doc comment list --node <DOC_ID> --type inline"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docValidateEnum(cmd, "resolve-status", []string{"resolved", "unresolved"}, "dws doc comment list --node <DOC_ID> --resolve-status unresolved")
|
||||
case "doc comment create":
|
||||
if err := docRequireNode(cmd, "dws doc comment create --node <DOC_ID> --content \"评论\""); err != nil {
|
||||
return err
|
||||
}
|
||||
return docRequire(cmd, "dws doc comment create --node <DOC_ID> --content \"评论\"", "content")
|
||||
case "doc comment reply", "doc comment update":
|
||||
if err := docRequireNode(cmd, "dws "+path+" --node <DOC_ID> --comment-key <COMMENT_KEY> --content \"内容\""); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docRequire(cmd, "dws "+path+" --node <DOC_ID> --comment-key <COMMENT_KEY> --content \"内容\"", "comment-key", "content"); err != nil {
|
||||
return err
|
||||
}
|
||||
if path == "doc comment reply" {
|
||||
emoji, _ := cmd.Flags().GetBool("emoji")
|
||||
groups, groupErr := commentGroupMentionIDs(cmd)
|
||||
if groupErr != nil {
|
||||
return groupErr
|
||||
}
|
||||
if emoji && len(groups) > 0 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--emoji cannot be used with --mentioned-open-conversation-id: emoji replies do not support group mentions", "表情回复请移除群 @;需要 @群时使用普通文字回复")
|
||||
}
|
||||
}
|
||||
case "doc comment delete":
|
||||
if err := docRequireNode(cmd, "dws doc comment delete --node <DOC_ID> --comment-key <COMMENT_KEY> --yes"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docRequire(cmd, "dws doc comment delete --node <DOC_ID> --comment-key <COMMENT_KEY> --yes", "comment-key")
|
||||
case "doc comment create-inline":
|
||||
if err := docRequireNode(cmd, "dws doc comment create-inline --node <DOC_ID> --block-id <BLOCK_ID> --start 0 --end 5 --content \"评论\""); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docRequire(cmd, "dws doc comment create-inline --node <DOC_ID> --block-id <BLOCK_ID> --start 0 --end 5 --content \"评论\"", "block-id", "content"); err != nil {
|
||||
return err
|
||||
}
|
||||
start, _ := cmd.Flags().GetInt("start")
|
||||
end, _ := cmd.Flags().GetInt("end")
|
||||
if !cmd.Flags().Changed("start") || !cmd.Flags().Changed("end") || start < 0 || end <= start {
|
||||
return docLocalError(cmd, CodeInvalidParam, "划词范围必须显式提供 --start/--end,且满足 0 <= start < end", "先读取块文本确认字符偏移。示例: dws doc comment create-inline --node <DOC_ID> --block-id <BLOCK_ID> --start 0 --end 5 --content \"评论\"")
|
||||
}
|
||||
case "doc export":
|
||||
if err := docRequireNode(cmd, "dws doc export --node <DOC_ID> --export-format pdf --output ./report.pdf"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docRequire(cmd, "dws doc export --node <DOC_ID> --export-format pdf --output ./report.pdf", "output"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docValidateEnum(cmd, "export-format", []string{"docx", "markdown", "md", "pdf"}, "dws doc export --node <DOC_ID> --export-format pdf --output ./report.pdf")
|
||||
case "doc export get":
|
||||
return docRequire(cmd, "dws doc export get --job-id <JOB_ID>", "job-id")
|
||||
case "doc import":
|
||||
if err := docValidateLocalFile(cmd, "file", "dws doc import --file ./report.docx --workspace <WORKSPACE_ID>"); err != nil {
|
||||
return err
|
||||
}
|
||||
if !deps.Caller.DryRun() && flagOrFallback(cmd, "folder", "folder-id") == "" && flagOrFallback(cmd, "workspace", "workspace-id") == "" {
|
||||
return docLocalError(cmd, CodeMissingParam, "导入文档必须提供 --folder 或 --workspace 作为目标位置", "先用 dws wiki space list --type myWikiSpace --format json 获取 workspaceId")
|
||||
}
|
||||
case "doc import get":
|
||||
return docRequire(cmd, "dws doc import get --task-id <TASK_ID>", "task-id")
|
||||
case "doc version save", "doc version list", "doc version revert":
|
||||
if err := docRequireNode(cmd, "dws "+path+" --node <DOC_ID> --format json"); err != nil {
|
||||
return err
|
||||
}
|
||||
if path == "doc version revert" && (!cmd.Flags().Changed("version")) {
|
||||
return docLocalError(cmd, CodeMissingParam, "缺少目标版本 --version", "先用 dws doc version list --node <DOC_ID> 获取真实版本号")
|
||||
}
|
||||
if path == "doc version revert" {
|
||||
version, _ := cmd.Flags().GetInt("version")
|
||||
if version < 1 {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--version 必须是大于 0 的真实版本号", "先用 dws doc version list --node <DOC_ID> 获取版本号")
|
||||
}
|
||||
}
|
||||
if path == "doc version list" {
|
||||
return docValidateLimit(cmd, 50, "dws doc version list --node <DOC_ID> --limit 10")
|
||||
}
|
||||
case "doc permission add", "doc permission update":
|
||||
if err := docRequireNode(cmd, "dws "+path+" --node <DOC_ID> --users uid1 --role EDITOR"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docValidatePermissionUsers(cmd, "dws "+path+" --node <DOC_ID> --users uid1 --role EDITOR"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docRequire(cmd, "dws "+path+" --node <DOC_ID> --users uid1 --role EDITOR", "role"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docValidateEnum(cmd, "role", []string{"MANAGER", "EDITOR", "DOWNLOADER", "READER"}, "dws "+path+" --node <DOC_ID> --users uid1 --role EDITOR")
|
||||
case "doc permission remove":
|
||||
if err := docRequireNode(cmd, "dws doc permission remove --node <DOC_ID> --users uid1"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docValidatePermissionUsers(cmd, "dws doc permission remove --node <DOC_ID> --users uid1")
|
||||
case "doc permission list":
|
||||
if err := docRequireNode(cmd, "dws doc permission list --node <DOC_ID> --limit 30"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := docValidateLimit(cmd, 200, "dws doc permission list --node <DOC_ID> --limit 30"); err != nil {
|
||||
return err
|
||||
}
|
||||
if roles, _ := cmd.Flags().GetString("filter-role"); roles != "" {
|
||||
for _, role := range parseRoleList(roles) {
|
||||
valid := false
|
||||
for _, candidate := range []string{"OWNER", "MANAGER", "EDITOR", "DOWNLOADER", "READER"} {
|
||||
if role == candidate {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return docLocalError(cmd, CodeInvalidParam, "--filter-role 包含非法角色 "+role, "仅支持 OWNER、MANAGER、EDITOR、DOWNLOADER、READER")
|
||||
}
|
||||
}
|
||||
}
|
||||
case "doc template list":
|
||||
if err := docValidateEnum(cmd, "source", []string{"MY", "PUBLIC"}, "dws doc template list --source MY"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docValidateLimit(cmd, 50, "dws doc template list --limit 20")
|
||||
case "doc template search":
|
||||
if flagOrFallback(cmd, "query", "keyword", "name") == "" {
|
||||
return docLocalError(cmd, CodeMissingParam, "缺少非空模板关键词 --query", "示例: dws doc template search --query \"周报\" --format json")
|
||||
}
|
||||
if err := docValidateEnum(cmd, "source", []string{"MY", "PUBLIC"}, "dws doc template search --query \"周报\" --source PUBLIC"); err != nil {
|
||||
return err
|
||||
}
|
||||
return docValidateLimit(cmd, 50, "dws doc template search --query \"周报\" --limit 20")
|
||||
case "doc template apply":
|
||||
if flagOrFallback(cmd, "template-id", "template", "tpl-id") == "" {
|
||||
return docLocalError(cmd, CodeMissingParam, "缺少模板 ID --template-id", "先用 dws doc template search --query \"关键词\" 获取真实 templateId")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func docLocalFileExtension(path string) string { return strings.ToLower(filepath.Ext(path)) }
|
||||
@@ -0,0 +1,137 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDocLocalPreflightRejectsBeforeMCP(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{"info-node", []string{"info"}, "--node"},
|
||||
{"read-node", []string{"read"}, "--node"},
|
||||
{"create-name", []string{"create"}, "--name"},
|
||||
{"create-content-conflict", []string{"create", "--name", "x", "--content", "a", "--content-file", "b"}, "不能同时"},
|
||||
{"create-file-missing", []string{"create", "--name", "x", "--content-file", "/definitely/not/found.md"}, "不可读取"},
|
||||
{"update-node", []string{"update", "--content", "x", "--mode", "append"}, "--node"},
|
||||
{"update-content-conflict", []string{"update", "--node", "n", "--content", "a", "--content-file", "b", "--mode", "append"}, "不能同时"},
|
||||
{"update-mode-missing", []string{"update", "--node", "n", "--content", "a"}, "--mode"},
|
||||
{"update-mode-invalid", []string{"update", "--node", "n", "--content", "a", "--mode", "merge"}, "append 或 overwrite"},
|
||||
{"update-index-mode", []string{"update", "--node", "n", "--content", "a", "--mode", "overwrite", "--index", "1"}, "--index"},
|
||||
{"block-list-node", []string{"block", "list"}, "--node"},
|
||||
{"block-list-range", []string{"block", "list", "--node", "n", "--start-index", "4", "--end-index", "2"}, "不能小于"},
|
||||
{"block-insert-node", []string{"block", "insert", "--text", "x"}, "--node"},
|
||||
{"block-insert-where", []string{"block", "insert", "--node", "n", "--text", "x", "--where", "middle"}, "before 或 after"},
|
||||
{"block-insert-ref", []string{"block", "insert", "--node", "n", "--text", "x", "--where", "before"}, "--ref-block"},
|
||||
{"block-insert-level", []string{"block", "insert", "--node", "n", "--heading", "x", "--level", "7"}, "1 到 6"},
|
||||
{"block-update-node", []string{"block", "update", "--block-id", "b", "--text", "x"}, "--node"},
|
||||
{"block-update-id", []string{"block", "update", "--node", "n", "--text", "x"}, "--block-id"},
|
||||
{"block-delete-id", []string{"block", "delete", "--node", "n"}, "--block-id"},
|
||||
{"media-download-resource", []string{"media", "download", "--node", "n"}, "--resource-id"},
|
||||
{"media-insert-file", []string{"media", "insert", "--node", "n"}, "--file"},
|
||||
{"comment-create-content", []string{"comment", "create", "--node", "n"}, "--content"},
|
||||
{"comment-reply-key", []string{"comment", "reply", "--node", "n", "--content", "x"}, "--comment-key"},
|
||||
{"comment-update-content", []string{"comment", "update", "--node", "n", "--comment-key", "k"}, "--content"},
|
||||
{"comment-delete-key", []string{"comment", "delete", "--node", "n"}, "--comment-key"},
|
||||
{"inline-block", []string{"comment", "create-inline", "--node", "n", "--content", "x", "--start", "0", "--end", "1"}, "--block-id"},
|
||||
{"inline-range", []string{"comment", "create-inline", "--node", "n", "--block-id", "b", "--content", "x", "--start", "2", "--end", "1"}, "start < end"},
|
||||
{"export-output", []string{"export", "--node", "n"}, "--output"},
|
||||
{"export-job", []string{"export", "get"}, "--job-id"},
|
||||
{"import-file", []string{"import", "--workspace", "w"}, "--file"},
|
||||
{"import-target", []string{"import", "--file", "/definitely/not/found.docx"}, "不可读取"},
|
||||
{"import-task", []string{"import", "get"}, "--task-id"},
|
||||
{"version-node", []string{"version", "list"}, "--node"},
|
||||
{"version-number", []string{"version", "revert", "--node", "n"}, "--version"},
|
||||
{"template-query", []string{"template", "search"}, "--query"},
|
||||
{"template-id", []string{"template", "apply"}, "--template-id"},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := newDocCommand()
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SilenceUsage = true
|
||||
cmd.SilenceErrors = true
|
||||
err := cmd.Execute()
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("error = %v, want containing %q", err, tc.want)
|
||||
}
|
||||
var cliErr *CLIError
|
||||
if !errors.As(err, &cliErr) || cliErr.ExitCode() != ExitValidation {
|
||||
t.Fatalf("error = %#v, want validation CLIError", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocLocalPreflightSecondBatchRejectsBeforeMCP(t *testing.T) {
|
||||
users31 := strings.TrimSuffix(strings.Repeat("u,", 31), ",")
|
||||
cases := []struct {
|
||||
name string
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{"search-created-negative", []string{"search", "--created-from", "-1"}, "非负毫秒"},
|
||||
{"search-created-range", []string{"search", "--created-from", "20", "--created-to", "10"}, "created-from"},
|
||||
{"search-visited-negative", []string{"search", "--visited-to", "-1"}, "非负毫秒"},
|
||||
{"search-visited-range", []string{"search", "--visited-from", "20", "--visited-to", "10"}, "visited-from"},
|
||||
{"search-limit-zero", []string{"search", "--limit", "0"}, "1 到 30"},
|
||||
{"search-limit-large", []string{"search", "--limit", "31"}, "1 到 30"},
|
||||
{"list-limit-zero", []string{"list", "--limit", "0"}, "1 到 50"},
|
||||
{"list-limit-large", []string{"list", "--limit", "51"}, "1 到 50"},
|
||||
{"read-depth-negative", []string{"read", "--node", "n", "--max-depth", "-1"}, "不能为负"},
|
||||
{"read-scope-invalid", []string{"read", "--node", "n", "--content-format", "jsonml", "--scope", "all"}, "outline"},
|
||||
{"read-scope-format", []string{"read", "--node", "n", "--scope", "outline"}, "content-format jsonml"},
|
||||
{"read-tags-missing", []string{"read", "--node", "n", "--content-format", "jsonml", "--scope", "tags"}, "--tags"},
|
||||
{"read-tags-wrong-scope", []string{"read", "--node", "n", "--content-format", "jsonml", "--scope", "outline", "--tags", "h1"}, "--tags only works"},
|
||||
{"read-range-start-missing", []string{"read", "--node", "n", "--content-format", "jsonml", "--scope", "range"}, "--start-block-id"},
|
||||
{"read-section-start-missing", []string{"read", "--node", "n", "--content-format", "jsonml", "--scope", "section"}, "--start-block-id"},
|
||||
{"read-markdown-output", []string{"read", "--node", "n", "--content-format", "markdown", "--output", "body.json"}, "Markdown 内容会直接显示在终端"},
|
||||
{"create-whitespace-name", []string{"create", "--name", " "}, "--name"},
|
||||
{"update-empty-content", []string{"update", "--node", "n", "--content", " ", "--mode", "append"}, "非空内容"},
|
||||
{"update-dry-run-append", []string{"update", "--node", "n", "--content", "x", "--mode", "append", "--dry-run"}, "仅用于预览 overwrite"},
|
||||
{"update-yes-append", []string{"update", "--node", "n", "--content", "x", "--mode", "append", "--yes"}, "仅用于确认 overwrite"},
|
||||
{"update-overwrite-confirm", []string{"update", "--node", "n", "--content", "x", "--mode", "overwrite"}, "必须加 --yes"},
|
||||
{"block-insert-content", []string{"block", "insert", "--node", "n"}, "必须提供一种块内容"},
|
||||
{"block-insert-text-heading", []string{"block", "insert", "--node", "n", "--text", "x", "--heading", "h"}, "不能同时"},
|
||||
{"block-insert-heading-element", []string{"block", "insert", "--node", "n", "--heading", "h", "--element", "{}"}, "不能同时"},
|
||||
{"block-insert-jsonml-text", []string{"block", "insert", "--node", "n", "--content-format", "jsonml", "--text", "x"}, "必须通过 --element"},
|
||||
{"block-insert-index", []string{"block", "insert", "--node", "n", "--text", "x", "--index", "-1"}, "--index 不能"},
|
||||
{"block-update-content", []string{"block", "update", "--node", "n", "--block-id", "b"}, "必须提供一种块内容"},
|
||||
{"block-update-text-element", []string{"block", "update", "--node", "n", "--block-id", "b", "--text", "x", "--element", "{}"}, "不能同时"},
|
||||
{"block-update-level", []string{"block", "update", "--node", "n", "--block-id", "b", "--heading", "h", "--level", "0"}, "1 到 6"},
|
||||
{"comment-limit-zero", []string{"comment", "list", "--node", "n", "--limit", "0"}, "1 到 50"},
|
||||
{"comment-limit-large", []string{"comment", "list", "--node", "n", "--limit", "51"}, "1 到 50"},
|
||||
{"comment-type", []string{"comment", "list", "--node", "n", "--type", "all"}, "global"},
|
||||
{"comment-status", []string{"comment", "list", "--node", "n", "--resolve-status", "open"}, "resolved"},
|
||||
{"reply-emoji-group", []string{"comment", "reply", "--node", "n", "--comment-key", "k", "--content", "x", "--emoji", "--mentioned-open-conversation-id", "g"}, "emoji replies do not support group mentions"},
|
||||
{"permission-users-missing", []string{"permission", "add", "--node", "n", "--role", "READER"}, "--users"},
|
||||
{"permission-users-large", []string{"permission", "add", "--node", "n", "--role", "READER", "--users", users31}, "最多处理 30"},
|
||||
{"permission-role", []string{"permission", "add", "--node", "n", "--role", "OWNER", "--users", "u"}, "MANAGER"},
|
||||
{"permission-filter-role", []string{"permission", "list", "--node", "n", "--filter-role", "ADMIN"}, "非法角色"},
|
||||
{"export-format", []string{"export", "--node", "n", "--output", "x", "--export-format", "html"}, "docx"},
|
||||
}
|
||||
if len(cases) != 39 {
|
||||
t.Fatalf("second batch has %d cases, want 39", len(cases))
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
cmd := newDocCommand()
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SilenceUsage = true
|
||||
cmd.SilenceErrors = true
|
||||
err := cmd.Execute()
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("error = %v, want containing %q", err, tc.want)
|
||||
}
|
||||
var cliErr *CLIError
|
||||
if !errors.As(err, &cliErr) || cliErr.ExitCode() != ExitValidation {
|
||||
t.Fatalf("error = %#v, want validation CLIError", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMultiWikiSkillDoesNotRequireYesForAppend(t *testing.T) {
|
||||
path := filepath.Join("..", "..", "skills", "multi", "dingtalk-wiki", "SKILL.md")
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read wiki skill: %v", err)
|
||||
}
|
||||
text := string(content)
|
||||
if strings.Contains(text, "--mode overwrite|append --content-file <tmp.md> --yes") {
|
||||
t.Fatal("wiki skill still applies --yes to append and overwrite indiscriminately")
|
||||
}
|
||||
if !strings.Contains(text, "--mode append --content-file <tmp.md>") ||
|
||||
!strings.Contains(text, "--mode overwrite --content-file <tmp.md> --yes") {
|
||||
t.Fatal("wiki skill must publish separate append and confirmed overwrite examples")
|
||||
}
|
||||
}
|
||||
@@ -326,6 +326,7 @@ func runImportCommand(cmd *cobra.Command, args []string, cfg importFlowConfig) e
|
||||
documentType, _ := result["documentType"].(string)
|
||||
finalResult := map[string]any{
|
||||
"success": true,
|
||||
"status": "completed",
|
||||
"taskId": taskID,
|
||||
"documentUrl": documentURL,
|
||||
"documentName": documentName,
|
||||
|
||||
@@ -181,7 +181,7 @@ func TestCrossPlatformCoverageSheetImportRunsSharedDocImportFlow(t *testing.T) {
|
||||
if err := json.Unmarshal([]byte(output), &payload); err != nil {
|
||||
t.Fatalf("sheet import stdout must be one JSON document: %v\n%s", err, output)
|
||||
}
|
||||
if payload["nodeId"] != "node-1" || payload["success"] != true {
|
||||
if payload["nodeId"] != "node-1" || payload["success"] != true || payload["status"] != "completed" {
|
||||
t.Fatalf("output missing success contract: %#v", payload)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/helpers"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut/chatmsg"
|
||||
)
|
||||
@@ -82,6 +84,18 @@ var ChatMembersGet = shortcut.Shortcut{
|
||||
{Kind: shortcut.ConstraintExactlyOne, Flags: []string{"users", "open-dingtalk-ids"}},
|
||||
},
|
||||
Tips: []string{`dws chat +chat-members-get --id <openConversationId> --users odid1,odid2`},
|
||||
Validate: func(rt *shortcut.RuntimeContext) error {
|
||||
for _, value := range rt.StrSliceFirst("users", "open-dingtalk-ids") {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" || !isOpenID(value) {
|
||||
return apperrors.NewValidation(fmt.Sprintf(
|
||||
"--users/--open-dingtalk-ids 只接受成员 openDingTalkId;%q 不是 openDingTalkId。请先查询人员并传返回的 openDingTalkId",
|
||||
value,
|
||||
))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
conversationID := rt.StrFirst("id", "group", "chat-id", "conversation-id", "open-conversation-id")
|
||||
return rt.CallMCP("list_group_member_by_ids", map[string]any{
|
||||
@@ -186,6 +200,12 @@ var ChatUpdateIcon = shortcut.Shortcut{
|
||||
{Name: "icon-media-id", Type: shortcut.FlagString, Desc: "群头像 mediaId(以 @ 开头)", Required: true},
|
||||
},
|
||||
Tips: []string{`dws chat +chat-update-icon --group <openConversationId> --icon-media-id <mediaId>`},
|
||||
Validate: func(rt *shortcut.RuntimeContext) error {
|
||||
if err := helpers.ValidateChatMediaID(rt.Str("icon-media-id")); err != nil {
|
||||
return apperrors.NewValidation("invalid --icon-media-id: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
return rt.CallMCP("update_group_icon", map[string]any{
|
||||
"openConversationId": rt.Str("group"),
|
||||
@@ -207,7 +227,8 @@ var ChatUpdateSettings = shortcut.Shortcut{
|
||||
{Name: "setting-key", Type: shortcut.FlagString, Desc: "群设置项 key,如 searchable / onlyAdminCanAtAll", Required: true},
|
||||
{Name: "status", Type: shortcut.FlagInt, Desc: "设置值:0=关闭,1=开启", Required: true},
|
||||
},
|
||||
Tips: []string{`dws chat +chat-update-settings --group <openConversationId> --setting-key searchable --status 1`},
|
||||
Tips: []string{`dws chat +chat-update-settings --group <openConversationId> --setting-key searchable --status 1`},
|
||||
Validate: validateChatUpdateSettings,
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
return rt.CallMCP("update_group_settings", map[string]any{
|
||||
"openConversationId": rt.Str("group"),
|
||||
@@ -217,6 +238,27 @@ var ChatUpdateSettings = shortcut.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
var supportedChatSettingKeys = map[string]struct{}{
|
||||
"authority": {}, "joinValidation": {}, "onlyAdminCanAtAll": {}, "searchable": {},
|
||||
"addFriendForbidden": {}, "toolbarStatus": {}, "pluginCustomizeVerify": {},
|
||||
"onlyAdminCanDING": {}, "allMembersCanCreateMcsConf": {}, "onlyAdminCanSetMsgTop": {},
|
||||
"onlyAdminCanPinMsg": {}, "onlyAdminCanSendFile": {}, "allMembersCanCreateCalendar": {},
|
||||
"groupEmailDisabled": {}, "groupRedEnvelopeSwitch": {}, "groupLiveAuthority": {},
|
||||
"groupBillAuthority": {},
|
||||
}
|
||||
|
||||
func validateChatUpdateSettings(rt *shortcut.RuntimeContext) error {
|
||||
key := strings.TrimSpace(rt.Str("setting-key"))
|
||||
if _, ok := supportedChatSettingKeys[key]; !ok {
|
||||
return apperrors.NewValidation(fmt.Sprintf("不支持的 --setting-key %q;请使用 chat group update-settings --help 中列出的设置项", key))
|
||||
}
|
||||
status := rt.Int("status")
|
||||
if status != 0 && status != 1 {
|
||||
return apperrors.NewValidation("--status 只允许 0(关闭)或 1(开启)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChatDismiss dismisses (destroys) a group (dismiss_group, im).
|
||||
var ChatDismiss = shortcut.Shortcut{
|
||||
Service: "chat",
|
||||
|
||||
@@ -54,6 +54,15 @@ func TestMessagesSendPublishesCompleteIdentityConstraintInputs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesSendUploadTargetUsesUploadInterfaceFields(t *testing.T) {
|
||||
if got := messagesSendUploadTarget("cid-1", ""); !reflect.DeepEqual(got, map[string]any{"openConversationId": "cid-1"}) {
|
||||
t.Fatalf("group upload target = %#v", got)
|
||||
}
|
||||
if got := messagesSendUploadTarget("", "D-open-1"); !reflect.DeepEqual(got, map[string]any{"openDingTalkId": "D-open-1"}) {
|
||||
t.Fatalf("direct upload target = %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageSafeResourceDownloadsStayReadOnly(t *testing.T) {
|
||||
for _, command := range []shortcut.Shortcut{MessagesMget, MessagesResourceDownload} {
|
||||
if command.Risk != shortcut.RiskRead {
|
||||
@@ -155,6 +164,9 @@ func TestCrossPlatformCoverageMessagesSendCurrentUserLocalFileFlow(t *testing.T)
|
||||
fake.calls[2].tool != "send_personal_message" {
|
||||
t.Fatalf("file flow calls = %#v", fake.calls)
|
||||
}
|
||||
if fake.calls[0].args["openConversationId"] != "cid" || fake.calls[1].args["openConversationId"] != "cid" {
|
||||
t.Fatalf("upload target args = %#v / %#v, want openConversationId", fake.calls[0].args, fake.calls[1].args)
|
||||
}
|
||||
send := fake.calls[2]
|
||||
if send.args["msgType"] != "file" || send.args["openConversationId"] != "cid" ||
|
||||
send.args["uuid"] != "file-key" {
|
||||
@@ -177,6 +189,32 @@ func TestCrossPlatformCoverageMessagesSendCurrentUserLocalFileFlow(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatShortcutInvalidInputsStopBeforeMCP(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
}{
|
||||
{name: "member numeric user id", args: []string{"chat", "+chat-members-get", "--id", "cid", "--users", "489149"}},
|
||||
{name: "icon local path", args: []string{"chat", "+chat-update-icon", "--group", "cid", "--icon-media-id", "./logo.png", "--yes"}},
|
||||
{name: "setting unknown key", args: []string{"chat", "+chat-update-settings", "--group", "cid", "--setting-key", "unknown", "--status", "1", "--yes"}},
|
||||
{name: "setting invalid status", args: []string{"chat", "+chat-update-settings", "--group", "cid", "--setting-key", "searchable", "--status", "2", "--yes"}},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fake := &larkAlignmentCaller{}
|
||||
helpers.InitDeps(fake)
|
||||
root := newPlatformCoverageRoot()
|
||||
root.SetArgs(tc.args)
|
||||
if err := root.Execute(); err == nil {
|
||||
t.Fatal("invalid command succeeded, want validation error")
|
||||
}
|
||||
if len(fake.calls) != 0 {
|
||||
t.Fatalf("MCP calls = %#v, want zero", fake.calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrossPlatformCoverageMessagesSendCurrentUserLocalFileDryRunAndFailures(t *testing.T) {
|
||||
t.Chdir(t.TempDir())
|
||||
if err := os.WriteFile("fixture.bin", []byte("x"), 0o600); err != nil {
|
||||
|
||||
@@ -420,8 +420,7 @@ func executeMessagesSendUserFile(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
targetArgs := map[string]any{}
|
||||
addMessagesSendUserTarget(targetArgs, group, openID)
|
||||
targetArgs := messagesSendUploadTarget(group, openID)
|
||||
idempotencyKey := messagesSendIdempotencyKey(rt)
|
||||
if rt.DryRun() {
|
||||
return rt.Output(map[string]any{
|
||||
@@ -490,6 +489,13 @@ func executeMessagesSendUserFile(
|
||||
})
|
||||
}
|
||||
|
||||
func messagesSendUploadTarget(group, openID string) map[string]any {
|
||||
if group != "" {
|
||||
return map[string]any{"openConversationId": group}
|
||||
}
|
||||
return map[string]any{"openDingTalkId": openID}
|
||||
}
|
||||
|
||||
func addMessagesSendUserTarget(params map[string]any, group, openID string) {
|
||||
if group != "" {
|
||||
params["openConversationId"] = group
|
||||
|
||||
@@ -65,7 +65,7 @@ var AtMe = shortcut.Shortcut{
|
||||
`dws chat +at-me`,
|
||||
`dws chat +at-me --days 3`,
|
||||
},
|
||||
Validate: chatshortcut.ValidateMessageResourceDownload,
|
||||
Validate: validateAtMe,
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
// Step 1 — look-back window [now-Nd, now] in epoch millis. days defaults
|
||||
// to 7; guard against non-positive overrides so the window stays sane.
|
||||
@@ -109,6 +109,23 @@ var AtMe = shortcut.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
func validateAtMe(rt *shortcut.RuntimeContext) error {
|
||||
if err := chatshortcut.ValidateMessageResourceDownload(rt); err != nil {
|
||||
return err
|
||||
}
|
||||
days := rt.Int("days")
|
||||
if days <= 0 {
|
||||
return localChatOptionError("invalid_lookback_window", "+at-me 的 --days 必须大于 0", "--days")
|
||||
}
|
||||
if days > 3650 {
|
||||
return localChatOptionError("lookback_window_too_large", "+at-me 的 --days 超出支持范围 1-3650", "--days")
|
||||
}
|
||||
if rt.Int("limit") <= 0 {
|
||||
return localChatOptionError("invalid_page_size", "+at-me 的 --limit 必须大于 0", "--limit")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// atMeMessageItems locates the message list inside a search_at_me_message
|
||||
// response, probing common container keys at the top level and nested under
|
||||
// "result". Returns nil when no list is found.
|
||||
|
||||
@@ -22,6 +22,22 @@ func TestPreferExactGroupMatches(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLooksLikeOpenConversationID(t *testing.T) {
|
||||
for _, value := range []string{
|
||||
"cidayZx5r0T+UiMi6NrO1048A==",
|
||||
"cidr0zKX5dvj/c0wDO+wupqyg==",
|
||||
} {
|
||||
if !looksLikeOpenConversationID(value) {
|
||||
t.Errorf("looksLikeOpenConversationID(%q) = false", value)
|
||||
}
|
||||
}
|
||||
for _, value := range []string{"项目冲刺", "cid项目群", "city project"} {
|
||||
if looksLikeOpenConversationID(value) {
|
||||
t.Errorf("looksLikeOpenConversationID(%q) = true", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveMemberTypes(t *testing.T) {
|
||||
users, bots, err := resolveMemberTypes(nil)
|
||||
if err != nil || !users || !bots {
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
||||
chatshortcut "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut/chat"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut/chatmsg"
|
||||
@@ -66,8 +67,10 @@ var ChatMessages = shortcut.Shortcut{
|
||||
{Name: "user", Type: shortcut.FlagString, Desc: "单聊对方的 userId,与 --group 互斥"},
|
||||
{Name: "open-dingtalk-id", Type: shortcut.FlagString, Desc: "单聊对方的 openDingTalkId,与 --group/--user 互斥"},
|
||||
{Name: "time", Type: shortcut.FlagString, Desc: "时间边界,如 \"2025-03-01 00:00:00\";省略时从当前时间向前读取最近消息"},
|
||||
{Name: "before", Type: shortcut.FlagString, Desc: "--time <值> --direction older 的兼容写法", Hidden: true},
|
||||
{Name: "limit", Type: shortcut.FlagInt, Desc: "每页拉取的消息条数(可选)"},
|
||||
{Name: "size", Type: shortcut.FlagInt, Desc: "--limit 的旧版别名", Hidden: true},
|
||||
{Name: "page-all", Type: shortcut.FlagBool, Desc: "暂不支持;请按返回游标显式翻页", Hidden: true},
|
||||
{Name: "direction", Type: shortcut.FlagString, Enum: []string{"newer", "older"}, Desc: "时间方向 newer/older;省略时为 older,从时间边界向前读取"},
|
||||
{Name: "no-reactions", Type: shortcut.FlagBool, Desc: "不输出消息 reaction(默认输出)"},
|
||||
}, chatshortcut.MessageResourceDownloadFlags()...),
|
||||
@@ -79,7 +82,7 @@ var ChatMessages = shortcut.Shortcut{
|
||||
`dws chat +chat-messages --user <userId> --time "2025-03-01 00:00:00" --limit 50`,
|
||||
`dws chat +chat-messages --group <openconversation_id> --direction older`,
|
||||
},
|
||||
Validate: chatshortcut.ValidateMessageResourceDownload,
|
||||
Validate: validateChatMessages,
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
// Step 1 — build params and pick the right tool. Param keys
|
||||
// (openconversation_id / userId / time / forward / limit) match the MCP server
|
||||
@@ -88,8 +91,8 @@ var ChatMessages = shortcut.Shortcut{
|
||||
params := map[string]any{}
|
||||
fallbackConversationID := ""
|
||||
|
||||
if rt.Changed("time") && rt.Str("time") != "" {
|
||||
params["time"] = rt.Str("time")
|
||||
if boundary := rt.StrFirst("time", "before"); boundary != "" {
|
||||
params["time"] = boundary
|
||||
} else {
|
||||
params["time"] = formatDingTalkMessageBoundary(time.Now())
|
||||
}
|
||||
@@ -99,7 +102,9 @@ var ChatMessages = shortcut.Shortcut{
|
||||
// direction newer/older maps to the tools' boolean `forward` param
|
||||
// (newer -> forward=true, older -> forward=false), matching chat.go's
|
||||
// resolveMessageForward.
|
||||
if rt.Changed("direction") {
|
||||
if rt.Changed("before") {
|
||||
params["forward"] = false
|
||||
} else if rt.Changed("direction") {
|
||||
switch strings.TrimSpace(strings.ToLower(rt.Str("direction"))) {
|
||||
case "newer":
|
||||
params["forward"] = true
|
||||
@@ -148,6 +153,45 @@ var ChatMessages = shortcut.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
func validateChatMessages(rt *shortcut.RuntimeContext) error {
|
||||
if err := chatshortcut.ValidateMessageResourceDownload(rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if rt.Changed("time") && rt.Changed("before") {
|
||||
return localChatOptionError("conflicting_time_options", "+chat-messages 的 --time 与 --before 不能同时使用", "--time", "--before")
|
||||
}
|
||||
if rt.Changed("before") && rt.Changed("direction") && strings.ToLower(rt.Str("direction")) != "older" {
|
||||
return localChatOptionError("incompatible_time_direction", "+chat-messages 的 --before 不能与 --direction newer 同时使用", "--before", "--direction")
|
||||
}
|
||||
if groupID := rt.StrFirst("group", "conversation-id", "id"); groupID != "" && looksLikeHumanGroupName(groupID) {
|
||||
flag := changedConversationIDFlag(rt)
|
||||
return localChatOptionError("group_name_used_as_conversation_id", "+chat-messages 的 "+flag+" 需要 openConversationId,当前值像群名", flag)
|
||||
}
|
||||
if (rt.Changed("limit") && rt.Int("limit") <= 0) || (rt.Changed("size") && rt.Int("size") <= 0) {
|
||||
flag := "--limit"
|
||||
if rt.Changed("size") {
|
||||
flag = "--size"
|
||||
}
|
||||
return localChatOptionError("invalid_page_size", "+chat-messages 的 "+flag+" 必须大于 0", flag)
|
||||
}
|
||||
if boundary := rt.StrFirst("time", "before"); boundary != "" && !validChatTime(boundary) {
|
||||
flag := "--time"
|
||||
if rt.Changed("before") {
|
||||
flag = "--before"
|
||||
}
|
||||
return localChatOptionError("invalid_time_boundary", "+chat-messages 的 "+flag+" 格式无效", flag)
|
||||
}
|
||||
if rt.Bool("page-all") {
|
||||
return apperrors.NewValidation(
|
||||
"+chat-messages 暂不支持 --page-all",
|
||||
apperrors.WithReason("page_all_not_supported"),
|
||||
apperrors.WithActions("先执行单页查询,再使用返回的游标继续翻页"),
|
||||
apperrors.WithExamples(`dws chat +chat-messages --group <openConversationId> --time "2026-07-30 16:51:39" --direction older --format json`),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// chatMessageItems defensively unwraps the message list from the response,
|
||||
// tolerating the common container keys and one level of nesting under a
|
||||
// "result"/"data" wrapper.
|
||||
|
||||
@@ -260,3 +260,117 @@ func TestCrossPlatformCoverageCompatibilityAliases(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendToGroupRejectsNonNameInputsBeforeMCP(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
argv []string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "conversation id used as group name",
|
||||
argv: []string{"chat", "+send-to-group", "--group", "cidayZx5r0T+UiMi6NrO1048A==", "--text", "你好", "--yes"},
|
||||
want: "只接受群名关键词",
|
||||
},
|
||||
{
|
||||
name: "at all belongs to messages send",
|
||||
argv: []string{"chat", "+send-to-group", "--group", "项目冲刺", "--text", "你好", "--at-all", "--yes"},
|
||||
want: "--at-all",
|
||||
},
|
||||
{
|
||||
name: "idempotency belongs to messages send",
|
||||
argv: []string{"chat", "+send-to-group", "--group", "项目冲刺", "--text", "你好", "--idempotency-key", "case-1", "--yes"},
|
||||
want: "--idempotency-key",
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fake := &platformCoverageCaller{}
|
||||
helpers.InitDeps(fake)
|
||||
root := newPlatformCoverageRoot()
|
||||
root.SetArgs(tc.argv)
|
||||
err := root.Execute()
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("Execute() error = %v, want %q", err, tc.want)
|
||||
}
|
||||
if len(fake.calls) != 0 {
|
||||
t.Fatalf("MCP calls = %#v, want none", fake.calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatMessagesBeforeAliasAndPageAllGuard(t *testing.T) {
|
||||
t.Run("before maps to time and older", func(t *testing.T) {
|
||||
fake := &platformCoverageCaller{}
|
||||
helpers.InitDeps(fake)
|
||||
root := newPlatformCoverageRoot()
|
||||
root.SetArgs([]string{"chat", "+chat-messages", "--group", "cid-1", "--before", "2026-07-30 16:51:39"})
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
call := fake.calls[len(fake.calls)-1]
|
||||
if call.args["time"] != "2026-07-30 16:51:39" || call.args["forward"] != false {
|
||||
t.Fatalf("args = %#v", call.args)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("page all is blocked before MCP", func(t *testing.T) {
|
||||
fake := &platformCoverageCaller{}
|
||||
helpers.InitDeps(fake)
|
||||
root := newPlatformCoverageRoot()
|
||||
root.SetArgs([]string{"chat", "+chat-messages", "--group", "cid-1", "--page-all"})
|
||||
err := root.Execute()
|
||||
if err == nil || !strings.Contains(err.Error(), "暂不支持 --page-all") {
|
||||
t.Fatalf("Execute() error = %v", err)
|
||||
}
|
||||
if len(fake.calls) != 0 {
|
||||
t.Fatalf("MCP calls = %#v, want none", fake.calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRelatedChatOptionsRejectBeforeMCP(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
argv []string
|
||||
want string
|
||||
}{
|
||||
{"group members rejects cid as name", []string{"chat", "+group-members", "--group", "cidayZx5r0T+UiMi6NrO1048A=="}, "--group"},
|
||||
{"members list rejects cid as name", []string{"chat", "+chat-members-list", "--group", "cidayZx5r0T+UiMi6NrO1048A=="}, "--group"},
|
||||
{"members list rejects name as cid", []string{"chat", "+chat-members-list", "--conversation-id", "测试群"}, "--conversation-id"},
|
||||
{"members list reports id alias", []string{"chat", "+chat-members-list", "--id", "测试群"}, "--id"},
|
||||
{"members list rejects unknown member type", []string{"chat", "+chat-members-list", "--group", "测试群", "--member-types", "admin"}, "--member-types"},
|
||||
{"unread chats rejects nonpositive count", []string{"chat", "+unread-chats", "--count", "0"}, "--count"},
|
||||
{"messages rejects time and before", []string{"chat", "+chat-messages", "--group", "cid", "--time", "2026-08-01", "--before", "2026-08-02"}, "--time"},
|
||||
{"messages rejects before newer", []string{"chat", "+chat-messages", "--group", "cid", "--before", "2026-08-02", "--direction", "newer"}, "--direction"},
|
||||
{"messages rejects group name as cid", []string{"chat", "+chat-messages", "--group", "测试群"}, "--group"},
|
||||
{"messages rejects nonpositive limit", []string{"chat", "+chat-messages", "--group", "cid", "--limit", "0"}, "--limit"},
|
||||
{"messages reports size alias", []string{"chat", "+chat-messages", "--group", "cid", "--size", "0"}, "--size"},
|
||||
{"messages rejects invalid time", []string{"chat", "+chat-messages", "--group", "cid", "--time", "yesterday-ish"}, "--time"},
|
||||
{"messages reports before alias", []string{"chat", "+chat-messages", "--group", "cid", "--before", "yesterday-ish"}, "--before"},
|
||||
{"messages reports conversation id alias", []string{"chat", "+chat-messages", "--conversation-id", "测试群"}, "--conversation-id"},
|
||||
{"thread replies rejects group name as cid", []string{"chat", "+thread-replies", "--group", "测试群", "--thread-id", "thread-1"}, "--group"},
|
||||
{"thread replies rejects nonpositive limit", []string{"chat", "+thread-replies", "--group", "cid", "--thread-id", "thread-1", "--limit", "0"}, "--limit"},
|
||||
{"thread replies rejects invalid time", []string{"chat", "+thread-replies", "--group", "cid", "--thread-id", "thread-1", "--time", "last-week"}, "--time"},
|
||||
{"at me rejects nonpositive days", []string{"chat", "+at-me", "--days", "0"}, "--days"},
|
||||
{"at me rejects oversized days", []string{"chat", "+at-me", "--days", "3651"}, "--days"},
|
||||
{"at me rejects nonpositive limit", []string{"chat", "+at-me", "--limit", "0"}, "--limit"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
fake := &platformCoverageCaller{}
|
||||
helpers.InitDeps(fake)
|
||||
root := newPlatformCoverageRoot()
|
||||
root.SetArgs(tc.argv)
|
||||
err := root.Execute()
|
||||
if err == nil || !strings.Contains(err.Error(), tc.want) {
|
||||
t.Fatalf("Execute() error = %v, want %q", err, tc.want)
|
||||
}
|
||||
if len(fake.calls) != 0 {
|
||||
t.Fatalf("MCP calls = %#v, want none", fake.calls)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,12 @@ var GroupMembers = shortcut.Shortcut{
|
||||
{Name: "group", Type: shortcut.FlagString, Desc: "群名称(搜群关键词,用群名里连续的核心词)", Required: true},
|
||||
},
|
||||
Tips: []string{`dws chat +group-members --group 项目冲刺`},
|
||||
Validate: func(rt *shortcut.RuntimeContext) error {
|
||||
if looksLikeOpenConversationID(rt.Str("group")) {
|
||||
return localChatOptionError("group_name_expected", "+group-members 的 --group 需要群名,当前值像 openConversationId", "--group")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
groupName := rt.Str("group")
|
||||
|
||||
@@ -121,6 +127,7 @@ var ChatMembersList = shortcut.Shortcut{
|
||||
`dws chat +chat-members-list --group "项目冲刺"`,
|
||||
`dws chat +chat-members-list --conversation-id <openConversationId> --member-types user,bot`,
|
||||
},
|
||||
Validate: validateChatMembersList,
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
groupID := strings.TrimSpace(rt.StrFirst("conversation-id", "id", "chat-id", "open-conversation-id"))
|
||||
groupName := strings.TrimSpace(rt.Str("group"))
|
||||
@@ -194,6 +201,25 @@ var ChatMembersList = shortcut.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
func validateChatMembersList(rt *shortcut.RuntimeContext) error {
|
||||
groupName := strings.TrimSpace(rt.Str("group"))
|
||||
groupID := strings.TrimSpace(rt.StrFirst("conversation-id", "id", "chat-id", "open-conversation-id"))
|
||||
if groupName != "" && looksLikeOpenConversationID(groupName) {
|
||||
return localChatOptionError("conversation_id_used_as_group_name", "+chat-members-list 的 --group 需要群名,当前值像 openConversationId", "--group")
|
||||
}
|
||||
if groupID != "" && looksLikeHumanGroupName(groupID) {
|
||||
flag := changedConversationIDFlag(rt)
|
||||
return localChatOptionError("group_name_used_as_conversation_id", "+chat-members-list 的 "+flag+" 需要 openConversationId,当前值像群名", flag)
|
||||
}
|
||||
if rt.Changed("member-types") {
|
||||
users, bots, err := resolveMemberTypes(rt.StrSlice("member-types"))
|
||||
if err != nil || (!users && !bots) {
|
||||
return localChatOptionError("invalid_member_types", "+chat-members-list 的 --member-types 包含不支持的值", "--member-types")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveGroupName(rt *shortcut.RuntimeContext, groupName string) (string, error) {
|
||||
data, err := rt.CallMCPData("im", "search_groups", map[string]any{
|
||||
"keyword": groupName,
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2026 Alibaba Group
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
package smart
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
apperrors "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/errors"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
||||
)
|
||||
|
||||
func looksLikeOpenConversationID(value string) bool {
|
||||
value = strings.TrimSpace(value)
|
||||
if !strings.HasPrefix(strings.ToLower(value), "cid") {
|
||||
return false
|
||||
}
|
||||
return len(value) >= 24 || strings.ContainsAny(value, "/+=")
|
||||
}
|
||||
|
||||
func looksLikeHumanGroupName(value string) bool {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" || looksLikeOpenConversationID(value) || strings.EqualFold(value, "cid") {
|
||||
return false
|
||||
}
|
||||
for _, r := range value {
|
||||
if unicode.Is(unicode.Han, r) || unicode.IsSpace(r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validChatTime(value string) bool {
|
||||
value = strings.TrimSpace(value)
|
||||
for _, layout := range []string{time.RFC3339, "2006-01-02 15:04:05", "2006-01-02"} {
|
||||
if _, err := time.Parse(layout, value); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func changedConversationIDFlag(rt *shortcut.RuntimeContext) string {
|
||||
for _, name := range []string{"group", "conversation-id", "id", "chat-id", "open-conversation-id"} {
|
||||
if rt.Changed(name) {
|
||||
return "--" + name
|
||||
}
|
||||
}
|
||||
return "会话 ID 参数"
|
||||
}
|
||||
|
||||
func localChatOptionError(reason, message string, flags ...string) error {
|
||||
flagText := strings.Join(flags, "、")
|
||||
action := "修正参数后重试,或查看当前命令帮助"
|
||||
if flagText != "" {
|
||||
action = "检查 " + flagText + " 后重试,或查看当前命令帮助"
|
||||
}
|
||||
return apperrors.NewValidation(
|
||||
message,
|
||||
apperrors.WithReason(reason),
|
||||
apperrors.WithActions(action),
|
||||
apperrors.WithExamples("dws chat --help"),
|
||||
)
|
||||
}
|
||||
@@ -45,9 +45,12 @@ var SendToGroup = shortcut.Shortcut{
|
||||
Flags: []shortcut.Flag{
|
||||
{Name: "group", Type: shortcut.FlagString, Desc: "群名称(搜群关键词,用群名里连续的核心词)", Required: true},
|
||||
{Name: "text", Type: shortcut.FlagString, Desc: "消息内容(支持 Markdown)", Required: true},
|
||||
{Name: "at-all", Type: shortcut.FlagBool, Desc: "不支持;需要 @所有人时改用 +messages-send", Hidden: true},
|
||||
{Name: "idempotency-key", Type: shortcut.FlagString, Desc: "不支持;需要幂等键时改用 +messages-send", Hidden: true},
|
||||
shortcut.AIMessageTagFlag(),
|
||||
},
|
||||
Tips: []string{`dws chat +send-to-group --group 项目冲刺 --text "今天 5 点前提交进度"`},
|
||||
Tips: []string{`dws chat +send-to-group --group 项目冲刺 --text "今天 5 点前提交进度"`},
|
||||
Validate: validateSendToGroup,
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
groupName := rt.Str("group")
|
||||
text := rt.Str("text")
|
||||
@@ -82,6 +85,35 @@ var SendToGroup = shortcut.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
func validateSendToGroup(rt *shortcut.RuntimeContext) error {
|
||||
group := rt.Str("group")
|
||||
if looksLikeOpenConversationID(group) {
|
||||
return apperrors.NewValidation(
|
||||
"+send-to-group 的 --group 只接受群名关键词,不能传 openConversationId",
|
||||
apperrors.WithReason("conversation_id_used_as_group_name"),
|
||||
apperrors.WithActions("已有群 openConversationId 时改用 chat +messages-send --chat-id"),
|
||||
apperrors.WithExamples(`dws chat +messages-send --chat-id <openConversationId> --text "消息内容" --format json`),
|
||||
)
|
||||
}
|
||||
if rt.Changed("at-all") || rt.Changed("idempotency-key") {
|
||||
unsupported := make([]string, 0, 2)
|
||||
if rt.Changed("at-all") {
|
||||
unsupported = append(unsupported, "--at-all")
|
||||
}
|
||||
if rt.Changed("idempotency-key") {
|
||||
unsupported = append(unsupported, "--idempotency-key")
|
||||
}
|
||||
flagText := strings.Join(unsupported, "、")
|
||||
return apperrors.NewValidation(
|
||||
"+send-to-group 不支持当前使用的发送选项:"+flagText,
|
||||
apperrors.WithReason("unsupported_send_to_group_option"),
|
||||
apperrors.WithActions("移除 "+flagText+",或改用支持该选项的消息发送命令"),
|
||||
apperrors.WithExamples(`dws chat +messages-send --help`),
|
||||
)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendGroupMatch is a single group candidate resolved from a name search.
|
||||
type sendGroupMatch struct {
|
||||
id string
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
package smart
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut"
|
||||
chatshortcut "github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut/chat"
|
||||
"github.com/DingTalk-Real-AI/dingtalk-workspace-cli/internal/shortcut/chatmsg"
|
||||
@@ -63,7 +65,7 @@ var ThreadReplies = shortcut.Shortcut{
|
||||
`dws chat +thread-replies --group <openconversationId> --thread-id <threadId>`,
|
||||
`dws chat +thread-replies --group <openconversationId> --thread-id <threadId> --time "2025-03-01 00:00:00" --limit 20`,
|
||||
},
|
||||
Validate: chatshortcut.ValidateMessageResourceDownload,
|
||||
Validate: validateThreadReplies,
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
// Step 1 — fetch the topic replies. Param keys (openconversationId /
|
||||
// topicId / startTime / pageSize) are copied verbatim from chat.go's
|
||||
@@ -105,6 +107,22 @@ var ThreadReplies = shortcut.Shortcut{
|
||||
},
|
||||
}
|
||||
|
||||
func validateThreadReplies(rt *shortcut.RuntimeContext) error {
|
||||
if err := chatshortcut.ValidateMessageResourceDownload(rt); err != nil {
|
||||
return err
|
||||
}
|
||||
if looksLikeHumanGroupName(rt.Str("group")) {
|
||||
return localChatOptionError("group_name_used_as_conversation_id", "+thread-replies 的 --group 需要 openConversationId,当前值像群名", "--group")
|
||||
}
|
||||
if rt.Changed("limit") && rt.Int("limit") <= 0 {
|
||||
return localChatOptionError("invalid_page_size", "+thread-replies 的 --limit 必须大于 0", "--limit")
|
||||
}
|
||||
if value := strings.TrimSpace(rt.Str("time")); value != "" && !validChatTime(value) {
|
||||
return localChatOptionError("invalid_time_boundary", "+thread-replies 的 --time 格式无效", "--time")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// threadReplyItems defensively unwraps the reply list from the response,
|
||||
// tolerating the common container keys and one level of nesting under a
|
||||
// "result"/"data" wrapper.
|
||||
|
||||
@@ -59,6 +59,12 @@ var UnreadChats = shortcut.Shortcut{
|
||||
`dws chat +unread-chats --count 20`,
|
||||
`dws chat +unread-chats --exclude-muted`,
|
||||
},
|
||||
Validate: func(rt *shortcut.RuntimeContext) error {
|
||||
if rt.Changed("count") && rt.Int("count") <= 0 {
|
||||
return localChatOptionError("invalid_page_size", "+unread-chats 的 --count 必须大于 0", "--count")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Execute: func(rt *shortcut.RuntimeContext) error {
|
||||
// Build params exactly like chatMessageListUnreadConversationsCmd: count is
|
||||
// only sent when > 0, excludeMuted only when true.
|
||||
|
||||
@@ -55,7 +55,12 @@ PRODUCT_END = "<!-- VISIBLE_SHORTCUTS_END -->"
|
||||
# only after verifying that the product skill has its own reviewed routing
|
||||
# section and intent table; compacting a sparse skill without an alternative
|
||||
# route would make its shortcuts harder to discover.
|
||||
COMPACT_PRODUCT_SERVICES = {"chat"}
|
||||
COMPACT_PRODUCT_SERVICES = {"aitable", "chat", "doc"}
|
||||
|
||||
# These compact products have every public Shortcut curated into Runtime
|
||||
# Schema. Keep this separate from COMPACT_PRODUCT_SERVICES because Chat still
|
||||
# has reviewed exclusions pending curation.
|
||||
FULLY_CURATED_COMPACT_PRODUCT_SERVICES = {"doc"}
|
||||
|
||||
def md_escape(value: Any) -> str:
|
||||
text = str(value or "")
|
||||
@@ -135,10 +140,22 @@ def product_section(service: str, rows: list[dict[str, Any]]) -> str:
|
||||
|
||||
|
||||
def compact_product_section(service: str, rows: list[dict[str, Any]]) -> str:
|
||||
if service in FULLY_CURATED_COMPACT_PRODUCT_SERVICES:
|
||||
inventory = (
|
||||
f"`{md_escape(service)}` 当前有 {len(rows)} 条公开 Shortcut,"
|
||||
"已全部进入 Runtime Schema。完整清单保留在 Runtime Shortcut "
|
||||
"Catalog,根 Skill 不重复展开;单条参数与安全契约按需查询 leaf Schema。"
|
||||
)
|
||||
else:
|
||||
inventory = (
|
||||
f"`{md_escape(service)}` 当前有 {len(rows)} 条公开 shortcut。"
|
||||
"完整清单保留在 Runtime Shortcut Catalog;已完成 Schema curation "
|
||||
"的子集可通过 leaf Schema 查询。高频产品根 Skill 不重复展开完整清单。"
|
||||
)
|
||||
return f"""{PRODUCT_START}
|
||||
## Shortcut 发现(按需)
|
||||
|
||||
`{md_escape(service)}` 当前有 {len(rows)} 条公开 shortcut。完整清单保留在 Runtime Shortcut Catalog;已完成 Schema curation 的子集可通过 leaf Schema 查询。高频产品根 Skill 不重复展开完整清单。已知意图直接使用下方的优先路由、意图表或任务 reference;命令已选中时直接执行,只在参数/安全语义不确定时读取 leaf Schema,在当前 Cobra flags 不确定时读取 leaf Help。
|
||||
{inventory}已知意图直接使用下方的优先路由、意图表或任务 reference;命令已选中时直接执行,只在参数/安全语义不确定时读取 leaf Schema,在当前 Cobra flags 不确定时读取 leaf Help。
|
||||
|
||||
仅当现有路由和 reference 都无法定位低频能力时,才执行 `dws shortcut list --service {md_escape(service)} --compact --format json` 做最后回退;不要为已知高频意图加载完整 Shortcut Catalog 或产品级 Schema。
|
||||
{PRODUCT_END}"""
|
||||
|
||||
@@ -7,8 +7,12 @@ cd "$ROOT"
|
||||
python3 scripts/gen_skill_shortcut_sections.py --check
|
||||
|
||||
chat_skill="skills/multi/dingtalk-chat/SKILL.md"
|
||||
aitable_skill="skills/multi/dingtalk-aitable/SKILL.md"
|
||||
doc_skill="skills/multi/dingtalk-doc/SKILL.md"
|
||||
mono_skill="skills/mono/SKILL.md"
|
||||
chat_max_bytes=14000
|
||||
aitable_max_bytes=11000
|
||||
doc_max_bytes=9500
|
||||
|
||||
chat_bytes="$(wc -c < "$chat_skill" | tr -d ' ')"
|
||||
if [ "$chat_bytes" -gt "$chat_max_bytes" ]; then
|
||||
@@ -17,6 +21,20 @@ if [ "$chat_bytes" -gt "$chat_max_bytes" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
aitable_bytes="$(wc -c < "$aitable_skill" | tr -d ' ')"
|
||||
if [ "$aitable_bytes" -gt "$aitable_max_bytes" ]; then
|
||||
printf '%s\n' \
|
||||
"skill context budget exceeded: $aitable_skill is ${aitable_bytes} bytes (max ${aitable_max_bytes})" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
doc_bytes="$(wc -c < "$doc_skill" | tr -d ' ')"
|
||||
if [ "$doc_bytes" -gt "$doc_max_bytes" ]; then
|
||||
printf '%s\n' \
|
||||
"skill context budget exceeded: $doc_skill is ${doc_bytes} bytes (max ${doc_max_bytes})" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
shortcut_rows="$(
|
||||
awk '
|
||||
/<!-- VISIBLE_SHORTCUTS_START -->/ { in_block = 1; next }
|
||||
@@ -31,6 +49,34 @@ if [ "$shortcut_rows" -ne 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
aitable_shortcut_rows="$(
|
||||
awk '
|
||||
/<!-- VISIBLE_SHORTCUTS_START -->/ { in_block = 1; next }
|
||||
/<!-- VISIBLE_SHORTCUTS_END -->/ { in_block = 0 }
|
||||
in_block && /^\|[[:space:]]*`/ { count++ }
|
||||
END { print count + 0 }
|
||||
' "$aitable_skill"
|
||||
)"
|
||||
if [ "$aitable_shortcut_rows" -ne 0 ]; then
|
||||
printf '%s\n' \
|
||||
"skill context budget exceeded: $aitable_skill re-expanded $aitable_shortcut_rows shortcut rows" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
doc_shortcut_rows="$(
|
||||
awk '
|
||||
/<!-- VISIBLE_SHORTCUTS_START -->/ { in_block = 1; next }
|
||||
/<!-- VISIBLE_SHORTCUTS_END -->/ { in_block = 0 }
|
||||
in_block && /^\|[[:space:]]*`/ { count++ }
|
||||
END { print count + 0 }
|
||||
' "$doc_skill"
|
||||
)"
|
||||
if [ "$doc_shortcut_rows" -ne 0 ]; then
|
||||
printf '%s\n' \
|
||||
"skill context budget exceeded: $doc_skill re-expanded $doc_shortcut_rows shortcut rows" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -Fq "充分阅读产品参考文件" "$mono_skill"; then
|
||||
printf '%s\n' \
|
||||
"skill context budget regression: $mono_skill requires full product-reference loading" >&2
|
||||
@@ -38,4 +84,4 @@ if grep -Fq "充分阅读产品参考文件" "$mono_skill"; then
|
||||
fi
|
||||
|
||||
printf '%s\n' \
|
||||
"skill context budget: ok (chat_bytes=$chat_bytes max=$chat_max_bytes shortcut_rows=$shortcut_rows)"
|
||||
"skill context budget: ok (chat_bytes=$chat_bytes max=$chat_max_bytes shortcut_rows=$shortcut_rows; aitable_bytes=$aitable_bytes max=$aitable_max_bytes shortcut_rows=$aitable_shortcut_rows; doc_bytes=$doc_bytes max=$doc_max_bytes shortcut_rows=$doc_shortcut_rows)"
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
> 通用规范见 [_common/conventions.md](_common/conventions.md)。
|
||||
|
||||
## 显式工作流
|
||||
|
||||
- 用户点名的 `create → list → insert/append/update` 是可观察命令链,必须保持顺序逐项执行;create 只承载明确的初始正文。有序列表块必须验证回读结构中的 `list.isOrdered=true`。
|
||||
- `--name` 不替代用户显式要求的正文 H1;新建资源返回 ID 后,同一请求的指代绑定该新资源,禁止搜索同名旧资源替换。
|
||||
- Word/Excel 需要“在线编辑/直接在线改”时使用 `doc import`,普通 `drive upload` 只保留原文件。
|
||||
|
||||
| Recipe | 行动指南(固定路线) |
|
||||
|--------|-------------------|
|
||||
| write-doc | 1. 按[「多源并行采集」](_common/conventions.md#多源并行采集公共模式)执行<br>2. **先把内容写入临时文件**(Linux/Mac `/tmp/<name>.md`,Windows `%TEMP%\<name>.md`)—— 含多行/表格/长文本必须走文件,不要把 markdown 直接作为命令行字符串<br>3. **单步创建**(< 200KB):`doc create --name "<文档名>" --content-file <tmp> [--folder <DOC_FOLDER_NODE_ID>] [--workspace <WS_ID>]`(`--folder` 只传文档文件夹 nodeId / alidocs 文件夹 URL,不传数字 dentryId)<br>4. **超长兜底**(> 200KB):**必须先向用户提示截断风险**(详见下方「分块 append 截断风险提示」),用户确认后再执行:`doc create --name "<文档名>" [--folder/--workspace]` → `nodeId` → 按段落切 ≤200KB 片段(不断表格) → 每片 `doc update --node <nodeId> --content-file <part> --mode append`<br>5. **回读校验**(必须):所有写入完成后,执行 `doc read --node <nodeId>` 回读文档,校验关键标题/段落是否完整写入(详见下方「doc update 回读校验规范」)<br>备选(仅短内容 <2KB 且无换行/表格):`doc create --name "..." --content "..."` |
|
||||
|
||||
@@ -665,8 +665,8 @@ Flags:
|
||||
- 知识库内 → `dws wiki node create --workspace <WS_ID> --type folder`(`doc folder create` / `doc file create --type folder` 已弃用)
|
||||
|
||||
用户说"上传文件/传文件/上传到文档/上传到知识库":
|
||||
- 上传 → `upload`(需本地文件路径)
|
||||
- 上传并转换 → `upload --convert`
|
||||
- 仅保留原始文件用于存储/下载 → `drive upload`(需本地文件路径)
|
||||
- 用户明确要求“在线编辑/大家直接在线改/转在线文档” → `doc import --file <本地路径>`;不得用普通 upload 的成功响应宣称可在线编辑
|
||||
|
||||
用户说"导入文件/导入为在线文档/导入 Word/导入 Excel/导入 xmind/导入 Markdown/把本地文件转在线文档":
|
||||
- 导入并转换为在线文档 → `doc import --file <本地路径>`
|
||||
@@ -742,8 +742,8 @@ Flags:
|
||||
关键区分: doc(文档编辑/阅读) vs aitable(数据表格操作) vs drive(钉盘文件管理)
|
||||
|
||||
用户说"上传文件/传文件/上传到文档/上传到知识库":
|
||||
- 上传 → `upload`(需本地文件路径)
|
||||
- 上传并转换 → `upload --convert`
|
||||
- 仅保留原始文件用于存储/下载 → `drive upload`(需本地文件路径)
|
||||
- 要转换为可在线编辑文档 → `doc import --file <本地路径>`,导入后验证在线类型与目标文件夹
|
||||
|
||||
用户说"下载文件/导出文件/下载到本地":
|
||||
- 下载 → `download`(需文件节点 ID 或 URL)
|
||||
@@ -1050,14 +1050,16 @@ EOF
|
||||
- `read` 返回的内容中,文档里的附件会以 OSS 临时下载链接形式给出(如 `https://alidocs2.oss-cn-zhangjiakou.aliyuncs.com/res/.../att/<resourceId>.ext?Expires=...`),该链接会过期。链接过期后,可从 URL 路径中提取 `<resourceId>`(即 `/att/` 后、扩展名前的 UUID 部分),然后使用 `media download --node <DOC_ID> --resource-id <resourceId>` 重新获取下载链接
|
||||
- `create` 不传 `--folder` 和 `--workspace` 时,默认创建在"我的文档"根目录
|
||||
- `create` 只能建"文档"(adoc);要建表格/脑图/白板/多维表/演示,用 `dws wiki node create --workspace <id> --type <type>`(`doc file create` 已弃用);建普通文件夹用 `dws drive mkdir`
|
||||
- `block list/insert/update/delete` 是块级精细编辑,适合结构化修改;简单内容追加建议用 `update --mode append`
|
||||
- `block list/insert/update/delete` 是块级精细编辑,适合结构化修改;只有用户未指定块操作的纯文本追加才建议 `update --mode append`。用户点名 list/insert/update/append 时必须逐项真实调用,不得折叠进 create
|
||||
- `block insert` 优先使用 `--text` 或 `--heading` 快捷方式;复杂块类型 (table, callout 等) 使用 `--element` JSON
|
||||
- 用户要求“有序列表块”时必须写真实列表结构(JSONML `p.list.isOrdered=true` 或等价 orderedList element),普通 Markdown/数字前缀段落不算完成
|
||||
- `--content` 参数中的换行必须使用**真实换行符**(即实际的换行字符,Unicode `U+000A`),而不是字面量字符串 `\n`(反斜杠加字母 n)。在通过程序或大模型构造此参数时,请确保字符串在发送前已正确反转义。如果传入的是两个字符的字面量 `\n`,所有内容将渲染在同一行,导致标题、段落和表格格式全部错乱。**含多行/表格/长文本时优先用 `--content-file path.md` 或 `--content -`(stdin),不经过 shell escape,换行和表格都保持原样**(详见下方「长 Markdown 写入」)。
|
||||
- 块类型包括: paragraph, heading, blockquote, callout, columns, orderedList, unorderedList, table, sheet, attachment, slot
|
||||
- 关键区分: doc(文档内容级操作) vs wiki(知识库空间级管理) vs aitable(数据表格操作) vs drive(钉盘文件管理)
|
||||
- wiki 是知识库容器,doc 是知识库中的文档内容;需要 `workspaceId` 时,先用 `dws wiki space list/search` 获取,再传给 doc 的 `--workspace` 参数
|
||||
- `doc upload vs drive upload`:用户提到"知识库/文档空间/workspace" → `doc upload`;提到"钉盘/网盘/我的文件" → `drive upload`;未明确目标时默认 `drive upload`
|
||||
- `upload` 支持上传任意类型文件 (PDF、Office、图片等) 到钉钉文档空间或知识库;`--convert` 可将 Office 文件转换为钉钉在线文档
|
||||
- `drive upload` / `doc upload` 是普通文件存储路径;用户要求 Word/Excel “在线编辑/直接在线改”时硬路由到 `doc import`,并验证导入后的在线类型和文件夹。只有用户明确同时要原文件与在线版时才分别 upload + import
|
||||
- 同一请求中新建、复制或导入返回的 `nodeId` 必须绑定后续“这篇/刚才那篇/上次那篇”;禁止搜索同名旧资源覆盖绑定
|
||||
- `--name` 只是文档外壳标题,不能替代用户显式要求的正文 H1;用户说“正文先起一级标题”时必须写入或插入真实 H1
|
||||
- `upload` 是三步自动完成的流程 (获取凭证 → OSS 上传 → 提交入库),无需手动分步操作
|
||||
- `download` 是两步自动完成的流程 (获取下载链接 → HTTP GET 下载),支持自动推断文件名;`--output` 可指定文件路径或目录
|
||||
- `media insert` 是三步自动完成的流程 (获取附件上传凭证 → OSS 上传 → 插入附件块到文档),无需手动分步操作
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
# doc block(块级精细编辑:list / insert / update / delete)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> 2. [`./style/doc-update-workflow.md`](./style/doc-update-workflow.md) — 改写流程(编辑形态优先级、JSONML validator 行为)
|
||||
> 3. [`./format/doc-jsonml-cookbook.md`](./format/doc-jsonml-cookbook.md) — JSONML 范例(含 callout / 分栏 / 表格 / 标题等节点的完整命令)
|
||||
> 4. [`./format/doc-jsonml-schema.md`](./format/doc-jsonml-schema.md) — JSONML 节点结构字段定义
|
||||
>
|
||||
> **同任务常配合**:[`doc-update.md`](./doc-update.md)(整篇 overwrite / 末尾追加纯文本)/ [`./format/doc-jsonml-cookbook.md`](./format/doc-jsonml-cookbook.md)(JSONML 复制范例)
|
||||
> 本文件自包含简单 list/insert/update/delete 契约,不要递归预读路由或 style reference。只有实际构造复杂 JSONML 节点时,才读取 [`doc-jsonml-cookbook.md`](./format/doc-jsonml-cookbook.md);字段仍不确定时再查 [`doc-jsonml-schema.md`](./format/doc-jsonml-schema.md)。整篇 overwrite 或纯文本 append 才转读 [`doc-update.md`](./doc-update.md)。
|
||||
|
||||
> **改写已有文档优先 JSONML**:保真度最高、callout / 分栏 / 表格 / @人 / 附件 / 颜色 / 嵌套都能 1:1 round-trip;写入端有 validator 兜底。详见 [`./style/doc-update-workflow.md` §1.3 编辑形态优先级](./style/doc-update-workflow.md)。
|
||||
|
||||
> **显式块操作不可折叠**:用户说“先 create,再 list/insert/update/append”时按原顺序真实调用;不能因为最终正文相似,就把后续块操作合并进 create 或一次 Markdown 写入。
|
||||
|
||||
---
|
||||
|
||||
## doc block list(查询块元素)
|
||||
@@ -173,7 +169,8 @@ dws doc block delete --node DOC_ID --block-id UUID
|
||||
|
||||
- **块类型**:paragraph、heading、blockquote、callout、columns、orderedList、unorderedList、table、sheet、attachment、slot。
|
||||
- **快捷 vs --element**:`block insert` 优先使用 `--text` 或 `--heading` 快捷方式;复杂块类型(table、callout、columns 等)使用 `--element` JSON 或 `--content-format jsonml`。
|
||||
- **简单内容追加**:建议用 [`./doc-update.md`](./doc-update.md) `--mode append`,不必走 block insert。
|
||||
- **有序列表块**:用户明确要求 ordered list / 有序列表块时,必须用 JSONML `p` 节点的 `list.isOrdered=true`(同一 `listId`;仅首项设 `start:1`)或等价原生 orderedList element;带 `1.` 前缀的普通段落、普通 Markdown 或一次 create 不满足要求。
|
||||
- **简单内容追加**:用户只说追加纯文本且不强调块操作时可用 [`./doc-update.md`](./doc-update.md) `--mode append`;用户明确说 block insert / 插入段落 / 插入标题 / 插入列表块时必须走 block insert。
|
||||
- **JSONML validator**(写入端默认行为):
|
||||
- 裸字符串、缺 uuid 等结构错误会被 validator 抦下并返回带 path 的错误(如 `$[2][2]: paragraph child must be span wrapper, got raw string.`)。
|
||||
- `--fix-jsonml` 开启 JSON 语法修复,推荐 agent 调用。
|
||||
@@ -241,6 +238,12 @@ dws doc block list --node <DOC_ID> --content-format jsonml --block-id <UUID>
|
||||
dws doc block insert --node <DOC_ID> --content-format jsonml --ref-block <UUID> --where after \
|
||||
--element '["p",{},["span",{"data-type":"text"},["span",{"data-type":"leaf"},"新段落"]]]'
|
||||
|
||||
# 插入有序列表块(3 项共用 listId,仅首项有 start)
|
||||
dws doc block insert --node <DOC_ID> --content-format jsonml \
|
||||
--element '["p",{"uuid":"ol1","list":{"listId":"actions","level":0,"isOrdered":true,"start":1}},["span",{"data-type":"text"},["span",{"data-type":"leaf"},"第一项"]]]'
|
||||
dws doc block insert --node <DOC_ID> --content-format jsonml \
|
||||
--element '["p",{"uuid":"ol2","list":{"listId":"actions","level":0,"isOrdered":true}},["span",{"data-type":"text"},["span",{"data-type":"leaf"},"第二项"]]]'
|
||||
|
||||
# 插入 callout(colorBlocks)
|
||||
dws doc block insert --node <DOC_ID> --content-format jsonml --ref-block <UUID> --where after \
|
||||
--element '["container",{"uuid":"co1","subType":"colorBlocks","metadata":{"bgcolor":"#FDE2E0","border":"#F5C2C7"}},["p",{"uuid":"co1p1"},["span",{"data-type":"text"},["span",{"data-type":"leaf"},"高风险操作,先备份"]]]]'
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
# doc comment(文档评论:list / create / reply / update / delete / create-inline)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
>
|
||||
> **同任务常配合**:`dws contact user search`(查 `--mention` 用 userId)/ `dws chat search`(查群用 openConversationId)/ [`doc-block.md`](./doc-block.md)(划词评论必须先取 blockId 与 paragraph 文本)
|
||||
> 本文件自包含评论命令契约。仅在需要 mention 时查询真实 userId/openConversationId;仅在划词评论尚无 blockId 与 paragraph 文本时读取 [`doc-block.md`](./doc-block.md) 并执行 block list。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
# doc create(创建文档)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> 2. [`./style/doc-create-workflow.md`](./style/doc-create-workflow.md) — 创建工作流(标题、位置、骨架、回读校验)
|
||||
> 3. [`./style/doc-style-guideline.md`](./style/doc-style-guideline.md) — 排版规范(草稿元素清单、骨架样板)
|
||||
> 4. [`./doc-update.md` §内容写入管道](./doc-update.md#内容写入管道createupdate-共用) — 长内容自动分片、`--content-file` vs `--content` 选择
|
||||
> 5. [`./format/doc-jsonml-cookbook.md`](./format/doc-jsonml-cookbook.md) — 仅当使用 `--content-format jsonml` 时必读
|
||||
> 本文件自包含普通 Markdown 创建契约,不要递归预读 `doc.md`、style 或 update reference。仅当用户要求复杂版式并实际选择 JSONML 时,读取 [`doc-jsonml-cookbook.md`](./format/doc-jsonml-cookbook.md);需要文档骨架建议时才读取对应 style 章节。
|
||||
|
||||
## 创建路由前置判断(必看)
|
||||
|
||||
@@ -40,7 +35,7 @@ Flags:
|
||||
|
||||
## 关键说明
|
||||
|
||||
- **`--name` 是 H1**:正文从 `##` 开始;正文内不要再写 `#` 一级标题(除非确需且已说明动机)。
|
||||
- **标题优先级**:`--name` 是文档外壳标题,默认可视作 H1;但它不能替代用户显式要求的正文一级标题。用户说“正文写 `# ...`”“正文先起个一级标题”时,必须在初始内容中保留该 `#` H1;用户未要求正文 H1 时,正文默认从 `##` 开始以避免重复。
|
||||
- 不传 `--folder` 和 `--workspace` 时,默认创建在「我的文档」根目录。
|
||||
- `--folder` 仅接受文档文件夹 `nodeId` / `dentryUuid` / alidocs 文件夹 URL;**禁止**传入 drive `dentryId`、`parentId`、`spaceId` 这类纯数字 ID。
|
||||
- 输入方式选择见 [`./doc-update.md` §内容写入管道](./doc-update.md#内容写入管道createupdate-共用)(与 update 共用)。短文本字面量可 `--content`,多行/表格/特殊字符必须 `--content-file` 或 `--content -`。
|
||||
@@ -54,6 +49,12 @@ Flags:
|
||||
| `docUrl` | 最终交付给用户的链接;缺失时用 [`./doc-info.md`](./doc-info.md) 补查 |
|
||||
| `chunksWritten` | 判断是否触发自动分片;> 1 时重点检查章节顺序 |
|
||||
|
||||
同一请求后续出现“这篇/刚才那篇/上次那篇”时,直接续用本次 create 返回的 `nodeId`;禁止先搜索同名文档再把后续操作指向旧节点。
|
||||
|
||||
## 显式操作序列
|
||||
|
||||
用户点名 `block list`、插入、追加、更新等后续动作时,必须按原顺序逐项执行。`doc create` 只写用户指定的初始内容,不能为了减少调用把后续标题、列表或段落提前塞进 create。例:`创建 → 查看块结构 → 末尾插入段落` 必须真实执行 create、block list、block insert 三步。
|
||||
|
||||
## 回读验收(必读)
|
||||
|
||||
CLI **不会**自动回读校验。**每次创建后**都必须执行 `doc read --node <nodeId>` 校验关键标题、段落首句、表格表头是否完整。详见 [`./style/doc-create-workflow.md` «回读验收»](./style/doc-create-workflow.md)。
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# doc export(在线文档导出为 docx)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> 本文件自包含 export 契约。已有当前请求返回的 adoc nodeId 时直接导出;目标类型未知时只执行一次 info 检查,不要递归读取 `doc.md`。
|
||||
|
||||
> **路由前置判断**:用户说「下载/导出」时**必须**先用 [`./doc-info.md`](./doc-info.md) `info --node <ID> --format json` 查 `contentType`:
|
||||
> - `contentType` 为 `ALIDOC`(在线文档)→ **必须用 `export`**,禁止用 `download`
|
||||
@@ -45,6 +44,7 @@ Flags:
|
||||
|
||||
## 关键说明
|
||||
|
||||
- 同一请求中刚执行 create/copy/import 并紧接着说“这篇/刚才那篇/上次那篇”时,`--node` 必须使用该写操作真实返回的新 `nodeId`;不得预先搜索同名文档,也不得用搜索结果中的旧节点替换它。
|
||||
- `export` 是一体化命令,一条命令自动完成提交→轮询→下载,**无需手动编排轮询**。CLI 内部使用渐进式退避轮询(最多约 5 分钟)。
|
||||
- `export` 超时或中断后,CLI 会输出 `jobId`,可用 `dws doc export get --job-id <jobId>` 手动查询任务状态。
|
||||
- `export` 当前仅支持钉钉在线文档(alidocs,`contentType=ALIDOC`)导出为 `docx`,**在线表格导出请使用其他命令**。
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# doc 文件操作(upload / download / copy / move / rename / delete + folder create)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> **按需使用**:本文件自包含弃用命令的兼容说明,不要求先读总路由;优先按下方提示改用 `drive` / `wiki`。
|
||||
|
||||
> **弃用提示(文件管理命令正在迁移到 drive / wiki)**:本文所列 `doc` 文件管理命令虽仍能跑,但执行时会打印弃用警告,请优先改用 `drive` / `wiki` 对应命令:
|
||||
> - `doc download` → **`dws drive download`**(下载已有文件;在线文档导出 docx 仍走 `doc export`)
|
||||
@@ -32,6 +31,7 @@ Flags:
|
||||
- `upload` 是三步自动完成的流程(获取凭证 → OSS 上传 → 提交入库),无需手动分步操作。
|
||||
- 支持上传任意类型文件(PDF、Office、图片等)到钉钉文档空间或知识库。
|
||||
- `--convert` 可将 Office 文件转换为钉钉在线文档。
|
||||
- **在线编辑硬路由**:用户说“大家直接在线改/上传后在线编辑/转成钉钉文档”时使用 [`./doc-import.md`](./doc-import.md) `doc import`,并回查在线类型;普通 `doc/drive upload` 只用于保留文件,不能据此承诺可在线编辑。
|
||||
- **`doc upload` vs `drive upload`**:用户提到「知识库 / 文档空间 / workspace」→ `doc upload`;提到「钉盘 / 网盘 / 我的文件」→ `drive upload`;未明确目标时默认 `drive upload`。
|
||||
- 与 [`./doc-media.md`](./doc-media.md) `media insert` 的区别:`upload` 上传到文档空间作为**独立文件**;`media insert` 作为**附件块插入到文档正文中**。
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
不要先读取文件内容再调用 `doc create` 或 `doc update`。`doc import` 会按文件格式走导入任务,保留更完整的原始结构。
|
||||
|
||||
> **在线编辑硬路由**:用户说“上传后在线编辑/大家直接在线改/转成钉钉文档”时必须使用 `doc import`。`drive upload` 只保留原始 `.docx/.xlsx/...` 普通文件,不能据此宣称已可在线编辑。若用户明确要同时保留原文件和在线版,才先 `drive upload`,再单独 `doc import`,并分别验证两个返回节点。
|
||||
|
||||
## 命令
|
||||
|
||||
```bash
|
||||
@@ -38,6 +40,7 @@ dws doc import get --task-id <TASK_ID> --format json
|
||||
3. 执行 `dws doc import --file ... --format json`。
|
||||
4. 正常情况下 CLI 会自动提交、上传并轮询导入任务。
|
||||
5. 如果命令超时或中断,从输出中提取 `taskId`,再执行 `dws doc import get --task-id <TASK_ID> --format json`。
|
||||
6. 用返回的 `documentUrl`/`nodeId` 执行 `drive info` 或 `doc info`,确认在线类型和目标文件夹;验证通过后才能说“可直接在线编辑”。
|
||||
|
||||
## 上下文传递
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# doc info(获取文档元信息 + URL 解析)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> 2. [`../../url-patterns.md`](../../url-patterns.md) — 仅当用户原始 `alidocs` URL 需要 probe 时
|
||||
> 本文件自包含已知 nodeId/URL 的 info 契约。只有原始 alidocs URL 类型仍不明确时,才读取 [`url-patterns.md`](../../url-patterns.md);不要递归读取 `doc.md`。
|
||||
>
|
||||
> **同任务常配合**:`dws drive search` / `dws wiki node search`(先定位 nodeId)/ [`doc-read.md`](./doc-read.md)(确认是 ALIDOC 后读正文)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# doc media(附件 / 图片:download / insert)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> 本文件自包含 media insert/download 契约。nodeId、文件路径或 resourceId 已知时直接执行;只在需要相对块定位且 blockId 未知时读取 [`doc-block.md`](./doc-block.md)。
|
||||
|
||||
> ⚠️ **图片插入硬规则**:
|
||||
> - 图片来源如果是钉盘/文档空间中的文件,**必须先下载到本地**(`dws drive download --node <图片nodeId> --output /tmp/xxx.png`),再执行 `media insert`
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# doc permission(文档权限:add / update / list)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> **按需使用**:本文件自包含文档节点权限命令;不要求先读总路由。知识库整体成员权限改读 `dingtalk-wiki`。
|
||||
|
||||
> **关键区分**:
|
||||
> - "把**某篇文档**授权给某人" → `doc permission add`(节点级,包括「我的文档」下的文档都支持)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
# doc read(读取文档内容)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> 2. [`./format/doc-jsonml-cookbook.md`](./format/doc-jsonml-cookbook.md) — 仅当使用 `--content-format jsonml` 时必读
|
||||
>
|
||||
> **同任务常配合**:[`doc-info.md`](./doc-info.md)(先解析 URL,确认 contentType=ALIDOC、extension=adoc)/ [`doc-update.md`](./doc-update.md)(读后改写)/ [`doc-block.md`](./doc-block.md)(块级精修前先读结构)
|
||||
> 本文件自包含普通 read 契约。用户已给当前 adoc nodeId/URL 时直接读取;类型未知时才先执行 info。选择 JSONML 只为获取结构,不要求预读 cookbook;实际构造 JSONML 写入时再按需加载。
|
||||
|
||||
## 命令格式
|
||||
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
# doc update(更新文档内容)
|
||||
|
||||
> **前置条件(MUST READ):** 执行本命令前,必须先用 Read 工具读取以下文件:
|
||||
> 1. [`../doc.md`](../doc.md) — 命令路由 + 场景索引 + 意图判断 + 工作流
|
||||
> 2. [`./style/doc-update-workflow.md`](./style/doc-update-workflow.md) — 改写流程(编辑形态优先级、分片 append、回读验收)
|
||||
> 3. [`./style/doc-style-guideline.md`](./style/doc-style-guideline.md) — 排版规范
|
||||
> 4. [`./format/doc-jsonml-cookbook.md`](./format/doc-jsonml-cookbook.md) — 仅当使用 `--content-format jsonml` 时必读
|
||||
>
|
||||
> **同任务常配合**:[`doc-read.md`](./doc-read.md)(改写前必读,jsonml 模式拿当前结构;担心被并发覆盖时再取 revision)/ [`doc-block.md`](./doc-block.md)(单 block 改写优先;本命令更适合追加 / 整篇 overwrite)
|
||||
> 本文件自包含普通 append/overwrite 契约,不要递归预读路由或 style reference。纯文本 append 可直接执行;overwrite 先 read/dry-run/确认。只有保真改写或复杂 JSONML 才读取 [`doc-update-workflow.md`](./style/doc-update-workflow.md) 与 cookbook;单块修改改用 [`doc-block.md`](./doc-block.md)。
|
||||
|
||||
## 命令格式
|
||||
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
|
||||
> 改写已有文档见 [doc-update-workflow.md](./doc-update-workflow.md)。排版规范见 [doc-style-guideline.md](./doc-style-guideline.md)。
|
||||
|
||||
## 前置必读
|
||||
## 按需使用
|
||||
|
||||
> **同时读取 [doc-style-guideline.md](./doc-style-guideline.md):**
|
||||
> - **§2.0 类型判断决策表** → 锁定文档类型(决策型 / 执行型 / 说明型 / 知识沉淀型)和骨架
|
||||
> - **§1 硬规则** → 全程生效(`--name` 已是 H1、不编造 URL、Markdown 草稿不写 callout 等)
|
||||
普通 Markdown 创建不需要先读本文件或 style guideline。只有用户要求设计文档骨架或复杂版式时,才查看下方对应章节;实际选择 JSONML 后再读取 cookbook。需要按文档类型选骨架时,按需读取 [doc-style-guideline.md](./doc-style-guideline.md) 的对应一节,不要通读。
|
||||
|
||||
### 关键词速查(用户意图 → 起稿路径)
|
||||
|
||||
@@ -45,7 +43,7 @@
|
||||
|
||||
| 项目 | 要求 |
|
||||
|------|------|
|
||||
| 标题 | 用 `--name` 传入;正文不要再重复同名一级标题 |
|
||||
| 标题 | 用 `--name` 传入;用户显式要求正文 H1 时原样保留,未要求时正文默认从 H2 开始 |
|
||||
| 位置 | 默认创建到我的文档;指定目录时只接受文档文件夹 `nodeId` 或 alidocs 文件夹 URL |
|
||||
| 正文 | 多行、表格、代码块、特殊字符或长度 >= 2KB 时必须写入 UTF-8 临时 `.md` 文件 |
|
||||
| 格式 | 按 §JSONML 起稿判定 决定起稿路径:命中 JSONML 起稿条件时**直接用 JSONML 构造**(跳过 markdown);未命中时用 Markdown 起稿,创建后按 [doc-update-workflow.md](./doc-update-workflow.md) 精修 |
|
||||
@@ -226,7 +224,7 @@ callout 可通过 `"showstk": true, "sticker": "图标名"` 配置顶部贴纸
|
||||
|
||||
> **脚手架策略警示**:Markdown 无法表达分栏/callout/色彩表头,拉回的 JSONML 只有纯文本骨架。精修阶段不是“在现有结构上加色”,而是“参照 RFC/Spec 重组结构”。
|
||||
|
||||
> **MUST READ**:动手写 JSONML 前,必须先用 Read 工具读取 [doc-jsonml-cookbook.md](../format/doc-jsonml-cookbook.md) — 其中 §决策型文档骨架范例 有可直接复制修改的完整模板。
|
||||
> **JSONML 条件加载**:仅在确定使用 JSONML 后读取 [doc-jsonml-cookbook.md](../format/doc-jsonml-cookbook.md);其中“决策型文档骨架范例”可直接改写。
|
||||
> 节点类型和属性的权威定义见 [doc-jsonml-schema.md](../format/doc-jsonml-schema.md)。
|
||||
|
||||
### ⚠️ JSONML 降级约束
|
||||
@@ -274,7 +272,7 @@ callout 可通过 `"showstk": true, "sticker": "图标名"` 配置顶部贴纸
|
||||
```
|
||||
|
||||
- 根节点固定 `"root"`(不是 `"body"`)
|
||||
- `--name` 已是 H1,JSONML 从 `h2` 开始
|
||||
- 用户未要求正文 H1 时,JSONML 从 `h2` 开始;用户明确要求“正文一级标题/插入一级标题”时必须构造 `h1`
|
||||
- 表格结构是 `table → tr → tc`(无 `th`/`td`)
|
||||
- 分栏是 `table` + `"sr": true`,`tc` 建议设 `fill` 背景色
|
||||
- 有序列表:仅第一项设 `"start": 1`,后续项不设 `start`(系统自动递增)
|
||||
@@ -315,7 +313,7 @@ dws doc read --node <nodeId> --content-format jsonml --output /tmp/<name>-readba
|
||||
- 只使用用户已提供或对话中已确认的正文素材。
|
||||
- 如果正文素材不足,先补齐文档目标、受众、章节和缺口;不要在本文中临时扩展跨产品采集流程。
|
||||
- **先按 [doc-style-guideline.md §2.0 类型判断决策表](./doc-style-guideline.md) 确定文档类型,再用对应类型的骨架样板(§2.1 决策型 / §2.2 执行型 / §2.3 说明型 / §2.4 知识沉淀型)**。不要套通用三段式。
|
||||
- **`--name` 已是 H1,正文从 `##` 开始**;正文内不要再写 `#` 一级标题(除非确实需要正文内再造一级 H1 并说明动机)。
|
||||
- **`--name` 是外壳标题,不覆盖显式正文 H1**:用户未要求正文一级标题时从 `##` 开始;用户明确给出 `# ...` 或要求“先起一级标题”时,正文必须保留该 H1。
|
||||
- 摘要、bullet、引用块、callout 等元素的使用边界以 style-guideline §3-§7 为准。
|
||||
- 同类信息保持一致:风险、状态、行动项各用一种元素 + 一种视觉语义(style-guideline §1.2 / §5)。
|
||||
- 临时文件必须保留真实换行,不能把换行写成字面量 `\n`。
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
## 一、硬规则
|
||||
|
||||
1. **`--name` 是 H1**:正文从 `##` 开始;正文内不写 `#`(除非确需正文内再造一级 H1 并说明动机)
|
||||
1. **用户显式正文 H1 优先**:`--name` 是文档外壳标题;用户未要求正文一级标题时从 `##` 开始。用户明确说“正文写 `# ...` / 先起一级标题 / 插入一级标题”时必须保留或插入真实 H1,不得用 `--name` 代替
|
||||
2. **同类信息同表达**:风险、状态、行动项、证据,每类只用一种元素 + 一种视觉语义(见 §5)
|
||||
3. **Markdown 草稿阶段只用稳定元素**:标题、段落、列表、checklist、表格、代码块;callout / 分栏 / 附件 / 复杂嵌套留到创建后用 `doc block insert` / `doc media insert` 精修
|
||||
4. **引用块只用于原文**:用户原话、会议摘录、外部材料原文;不许包装作者自己的结论
|
||||
@@ -209,7 +209,7 @@
|
||||
|
||||
### 4.1 标题与段落
|
||||
|
||||
- 正文从 `##` 开始(H1 已被 `--name` 占用)
|
||||
- 用户未要求正文 H1 时从 `##` 开始;用户明确要求正文 H1 时按原文使用 `#` 或 heading level 1
|
||||
- 标题层级 ≤ 4 层(§7)
|
||||
- 单段过长先拆段,再考虑换元素
|
||||
|
||||
@@ -217,6 +217,7 @@
|
||||
|
||||
- 普通列表:并列要点
|
||||
- 有序列表:顺序步骤
|
||||
- 用户明确要求“有序列表块”时必须使用真实列表结构;JSONML 为带 `list.isOrdered=true` 的多个 `p` 节点,不能只写带数字前缀的普通段落或以整篇 Markdown 代替显式 block insert
|
||||
- checklist:待办状态(含 `- [ ]` / `- [x]`)
|
||||
|
||||
列表项里开始出现"负责人 / 截止时间 / 状态"这类字段时,改用表格。
|
||||
|
||||
@@ -1,181 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
在指定目录创建文档并写入 Markdown 内容(一键完成)
|
||||
"""用原生 dws 写入管道创建文档,并回读验证。"""
|
||||
|
||||
用法:
|
||||
python doc_create_and_write.py \
|
||||
--name "项目周报" \
|
||||
--content "# 本周总结\n\n## 完成事项\n- 任务A"
|
||||
from __future__ import annotations
|
||||
|
||||
python doc_create_and_write.py \
|
||||
--name "会议纪要" \
|
||||
--content-file notes.md
|
||||
|
||||
python doc_create_and_write.py \
|
||||
--name "知识库文档" --content "# 内容" --folder FOLDER_ID
|
||||
|
||||
python doc_create_and_write.py --name "test" --content "hello" --dry-run
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import time
|
||||
import subprocess
|
||||
import argparse
|
||||
import json
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import List, Any, Optional
|
||||
from typing import Any, Optional, Sequence
|
||||
|
||||
|
||||
def run_dws(
|
||||
args: List[str], dry_run: bool = False,
|
||||
) -> Optional[Any]:
|
||||
cmd = ['dws'] + args
|
||||
class ScriptError(RuntimeError):
|
||||
"""可预期的脚本执行错误。"""
|
||||
|
||||
|
||||
def decode_json_output(output: str) -> Any:
|
||||
"""解析 JSON;兼容长内容写入前置的进度行。"""
|
||||
text = output.strip()
|
||||
if not text:
|
||||
raise ScriptError("dws 未返回 JSON")
|
||||
try:
|
||||
return json.loads(text)
|
||||
except json.JSONDecodeError:
|
||||
decoder = json.JSONDecoder()
|
||||
for offset, character in enumerate(text):
|
||||
if character not in "[{":
|
||||
continue
|
||||
try:
|
||||
value, end = decoder.raw_decode(text, offset)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
if not text[end:].strip():
|
||||
return value
|
||||
raise ScriptError("dws 返回的不是合法 JSON")
|
||||
|
||||
|
||||
def run_dws(args: Sequence[str], dry_run: bool = False) -> Any:
|
||||
"""执行一条 dws 命令,并把命令/业务失败统一转成 ScriptError。"""
|
||||
command = ["dws", *args]
|
||||
if dry_run:
|
||||
print(f"[dry-run] {' '.join(cmd)}")
|
||||
return {'dry_run': True}
|
||||
print(f"[dry-run] {shlex.join(command)}")
|
||||
return {"dry_run": True}
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd, capture_output=True, text=True, timeout=60
|
||||
command,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(f" ✗ 错误:{result.stderr.strip()}")
|
||||
return None
|
||||
return json.loads(result.stdout)
|
||||
except (subprocess.TimeoutExpired, json.JSONDecodeError,
|
||||
FileNotFoundError) as e:
|
||||
print(f" ✗ 错误:{e}")
|
||||
return None
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError) as exc:
|
||||
raise ScriptError(f"执行 dws 失败:{exc}") from exc
|
||||
if result.returncode != 0:
|
||||
detail = result.stderr.strip() or result.stdout.strip()
|
||||
raise ScriptError(
|
||||
f"dws 命令失败:{detail or f'退出码 {result.returncode}'}"
|
||||
)
|
||||
data = decode_json_output(result.stdout)
|
||||
if isinstance(data, dict) and data.get("success") is False:
|
||||
detail = data.get("errorMsg") or data.get("message") or "未知错误"
|
||||
raise ScriptError(f"dws 业务调用失败:{detail}")
|
||||
return data
|
||||
|
||||
|
||||
def run_dws_with_retry(
|
||||
args: List[str],
|
||||
dry_run: bool = False,
|
||||
max_retries: int = 3,
|
||||
retry_delay: float = 1.0,
|
||||
) -> Optional[Any]:
|
||||
"""带重试机制的 dws 命令执行"""
|
||||
last_error = None
|
||||
for attempt in range(1, max_retries + 1):
|
||||
result = run_dws(args, dry_run=dry_run)
|
||||
if result is not None:
|
||||
return result
|
||||
if attempt < max_retries:
|
||||
print(f" ⚠️ 第 {attempt} 次尝试失败,{retry_delay}秒后重试...")
|
||||
time.sleep(retry_delay)
|
||||
retry_delay *= 1.5 # 指数退避
|
||||
return None
|
||||
def first_value(payload: Any, keys: Sequence[str]) -> str:
|
||||
"""从嵌套响应中提取第一个非空稳定字段。"""
|
||||
if isinstance(payload, dict):
|
||||
for key in keys:
|
||||
value = payload.get(key)
|
||||
if value is not None and str(value).strip():
|
||||
return str(value).strip()
|
||||
for value in payload.values():
|
||||
found = first_value(value, keys)
|
||||
if found:
|
||||
return found
|
||||
elif isinstance(payload, list):
|
||||
for value in payload:
|
||||
found = first_value(value, keys)
|
||||
if found:
|
||||
return found
|
||||
return ""
|
||||
|
||||
|
||||
def main():
|
||||
def run(argv: Optional[Sequence[str]] = None) -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description='创建文档并写入内容'
|
||||
description="使用 dws doc create 创建文档并回读验证"
|
||||
)
|
||||
parser.add_argument('--name', required=True, help='文档名称')
|
||||
parser.add_argument('--content', default='', help='Markdown 内容')
|
||||
parser.add_argument('--content-file', default='', help='内容文件')
|
||||
parser.add_argument('--folder', default='', help='目标文件夹 ID 或 URL')
|
||||
parser.add_argument('--workspace', default='', help='目标知识库 ID')
|
||||
parser.add_argument(
|
||||
'--mode', default='append', choices=['overwrite', 'append'],
|
||||
help='写入模式: overwrite=覆盖, append=追加 (默认 append)',
|
||||
parser.add_argument("--name", required=True, help="文档名称")
|
||||
content_group = parser.add_mutually_exclusive_group(required=True)
|
||||
content_group.add_argument("--content", help="Markdown 内容")
|
||||
content_group.add_argument("--content-file", help="UTF-8 Markdown 文件")
|
||||
location_group = parser.add_mutually_exclusive_group()
|
||||
location_group.add_argument(
|
||||
"--folder", default="", help="目标文档文件夹 ID 或 URL"
|
||||
)
|
||||
parser.add_argument(
|
||||
'--max-retries', type=int, default=3,
|
||||
help='每块写入失败时的最大重试次数 (默认 3)',
|
||||
location_group.add_argument(
|
||||
"--workspace", default="", help="目标知识库 ID 或 URL"
|
||||
)
|
||||
parser.add_argument('--dry-run', action='store_true')
|
||||
args = parser.parse_args()
|
||||
parser.add_argument("--dry-run", action="store_true")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
content = args.content
|
||||
supplied_path: Optional[Path] = None
|
||||
temporary_path: Optional[Path] = None
|
||||
if args.content_file:
|
||||
p = Path(args.content_file)
|
||||
if not p.exists():
|
||||
print(f"错误:文件不存在: {p}")
|
||||
sys.exit(1)
|
||||
content = p.read_text(encoding='utf-8')
|
||||
if not content:
|
||||
print('错误:需要 --content 或 --content-file')
|
||||
sys.exit(1)
|
||||
chunk_size = 30000
|
||||
supplied_path = Path(args.content_file)
|
||||
if not supplied_path.is_file():
|
||||
raise ScriptError(f"内容文件不存在:{supplied_path}")
|
||||
elif not args.content or not args.content.strip():
|
||||
raise ScriptError("--content 不能为空")
|
||||
|
||||
create_args = ['doc', 'create', '--name', args.name, '--format', 'json']
|
||||
if args.folder:
|
||||
create_args.extend(['--folder', args.folder])
|
||||
if args.workspace:
|
||||
create_args.extend(['--workspace', args.workspace])
|
||||
try:
|
||||
if supplied_path is None and not args.dry_run:
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w", encoding="utf-8", suffix=".md", delete=False
|
||||
) as handle:
|
||||
handle.write(args.content)
|
||||
temporary_path = Path(handle.name)
|
||||
supplied_path = temporary_path
|
||||
|
||||
print(f'\n📝 创建文档: {args.name}')
|
||||
create_data = run_dws(create_args, dry_run=args.dry_run)
|
||||
content_path = str(supplied_path) if supplied_path else "<TEMP_CONTENT.md>"
|
||||
create_args = [
|
||||
"doc", "create",
|
||||
"--name", args.name,
|
||||
"--content-file", content_path,
|
||||
"--content-format", "markdown",
|
||||
"--format", "json",
|
||||
]
|
||||
if args.folder:
|
||||
create_args.extend(["--folder", args.folder])
|
||||
if args.workspace:
|
||||
create_args.extend(["--workspace", args.workspace])
|
||||
|
||||
node_id = None
|
||||
if not args.dry_run:
|
||||
if not create_data:
|
||||
sys.exit(1)
|
||||
node_id = (create_data.get('nodeId')
|
||||
or create_data.get('dentryUuid')
|
||||
or create_data.get('id', ''))
|
||||
print(f" ✓ 文档已创建 (ID: {node_id})")
|
||||
created = run_dws(create_args, dry_run=args.dry_run)
|
||||
node_id = "<NODE_ID>" if args.dry_run else first_value(
|
||||
created, ("nodeId", "dentryUuid")
|
||||
)
|
||||
if not node_id:
|
||||
raise ScriptError("文档创建响应缺少 nodeId,无法验证")
|
||||
|
||||
if len(content) <= chunk_size:
|
||||
mode_label = '追加' if args.mode == 'append' else '覆盖'
|
||||
print(f'\n✍️ 写入内容 (模式: {mode_label}, {len(content)} 字符)...')
|
||||
write_data = run_dws([
|
||||
'doc', 'update',
|
||||
'--node', node_id or '<NODE_ID>',
|
||||
'--content', content,
|
||||
'--mode', args.mode,
|
||||
'--format', 'json',
|
||||
], dry_run=args.dry_run)
|
||||
if write_data:
|
||||
print(f" ✓ 内容已写入 ({len(content)} 字符)")
|
||||
else:
|
||||
chunks = []
|
||||
pos = 0
|
||||
while pos < len(content):
|
||||
end = min(pos + chunk_size, len(content))
|
||||
if end < len(content):
|
||||
newline_pos = content.rfind('\n', pos, end)
|
||||
if newline_pos > pos:
|
||||
end = newline_pos + 1
|
||||
chunks.append(content[pos:end])
|
||||
pos = end
|
||||
info = run_dws(
|
||||
["doc", "info", "--node", node_id, "--format", "json"],
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
run_dws(
|
||||
["doc", "read", "--node", node_id, "--format", "json"],
|
||||
dry_run=args.dry_run,
|
||||
)
|
||||
if args.dry_run:
|
||||
return 0
|
||||
|
||||
total_chunks = len(chunks)
|
||||
print(f'\n✍️ 内容较长 ({len(content)} 字符), 分 {total_chunks} 块写入...')
|
||||
|
||||
success_chunks = 0
|
||||
for idx, chunk in enumerate(chunks):
|
||||
chunk_mode = args.mode if idx == 0 else 'append'
|
||||
write_data = run_dws_with_retry(
|
||||
[
|
||||
'doc', 'update',
|
||||
'--node', node_id or '<NODE_ID>',
|
||||
'--content', chunk,
|
||||
'--mode', chunk_mode,
|
||||
'--format', 'json',
|
||||
],
|
||||
dry_run=args.dry_run,
|
||||
max_retries=args.max_retries,
|
||||
)
|
||||
if write_data:
|
||||
print(f" ✓ 块 {idx + 1}/{total_chunks} 已写入 ({len(chunk)} 字符)")
|
||||
success_chunks += 1
|
||||
elif not args.dry_run:
|
||||
# 写入失败,报告部分写入状态
|
||||
print(f"\n❌ 块 {idx + 1}/{total_chunks} 写入失败(已重试 {args.max_retries} 次)")
|
||||
print(f"\n⚠️ 文档处于部分写入状态:")
|
||||
print(f" - 文档 ID: {node_id}")
|
||||
print(f" - 已写入: {success_chunks}/{total_chunks} 块")
|
||||
print(f" - 失败位置: 第 {idx + 1} 块")
|
||||
if args.mode == 'overwrite':
|
||||
print(f" - 模式: 覆盖模式,文档可能包含不完整内容")
|
||||
print(f" - 建议: 手动检查文档内容,或删除后重新创建")
|
||||
else:
|
||||
print(f" - 模式: 追加模式,已写入内容已保存")
|
||||
print(f" - 建议: 可手动补充剩余内容,或重新运行脚本")
|
||||
sys.exit(1)
|
||||
print('\n✅ 完成!')
|
||||
summary = {
|
||||
"success": True,
|
||||
"nodeId": node_id,
|
||||
"docUrl": first_value(info, ("docUrl", "documentUrl", "url"))
|
||||
or first_value(created, ("docUrl", "documentUrl", "url")),
|
||||
"chunksWritten": first_value(created, ("chunksWritten",)),
|
||||
"verified": True,
|
||||
}
|
||||
print(json.dumps(summary, ensure_ascii=False))
|
||||
return 0
|
||||
finally:
|
||||
if temporary_path is not None:
|
||||
temporary_path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def main() -> None:
|
||||
try:
|
||||
raise SystemExit(run())
|
||||
except ScriptError as exc:
|
||||
print(f"错误:{exc}", file=sys.stderr)
|
||||
raise SystemExit(1) from exc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: dingtalk-aitable
|
||||
description: 钉钉 AI 表格(多维表)。Use when 用户说 AI表格/多维表/数据表/base/table/建表/查记录/写数据/字段/记录增删改查/筛选/排序/公式/模板搜索/批量导入CSV或JSON/导出/仪表盘/图表/上传附件到表格/按字段类型建表。不做电子表格单元格读写(走 dingtalk-misc)、文档编辑(走 dingtalk-doc);听记待办入表先用 dingtalk-minutes 提取,再由本 skill 写入。命令前缀:dws aitable。
|
||||
description: 钉钉 AI 表格(多维表)。Use when 用户说 AI表格/多维表/Base/数据表/字段/记录增删改查/筛选排序/公式与跨表引用/视图表单/仪表盘图表/高级权限/自动化工作流/模板/CSV或JSON批量导入/Excel导入导出/记录附件。不做电子表格单元格读写与工作表公式(走 dingtalk-sheet)、普通文档编辑(走 dingtalk-doc)或钉盘文件管理(走 dingtalk-drive);听记待办入表先用 dingtalk-minutes 提取,再由本 skill 写入。命令前缀:dws aitable。
|
||||
metadata:
|
||||
cli_version: ">=0.2.14"
|
||||
category: product
|
||||
@@ -11,150 +11,111 @@ metadata:
|
||||
|
||||
# 钉钉 AI 表格 Skill
|
||||
|
||||
## 前置条件 — 执行操作前必读
|
||||
## 执行入口
|
||||
|
||||
> **CRITICAL — 执行任何 `dws` 操作前,MUST 先用 Read 工具完整读取 [`dws-shared`](../dws-shared/SKILL.md)。**该轻量文件包含全局执行契约、安全底线及 shared references 的按需加载导航;不要预加载其全部 references。
|
||||
执行任何 `dws` 操作前,完整读取 [`dws-shared`](../dws-shared/SKILL.md),但不要预加载其 references。高频意图直接使用本文件骨架;仅特殊参数、复杂数据形态或边界不明时读取一个 branch reference。
|
||||
|
||||
> 命令参考:[aitable.md](references/aitable.md);复杂命令按需加载 `references/aitable/*.md`;剧本:[06-data-analytics.md](references/06-data-analytics.md)。
|
||||
## 加载与路由顺序
|
||||
|
||||
1. 命中下方高频意图时直接使用精确骨架,不先查 Help 或产品级 Schema。
|
||||
2. 路由优先级固定为:精确骨架 / recipe > 匹配的公开 Shortcut > 原子命令。脚本只用于 Runtime 尚未覆盖的批量、文件传输或异步编排,不与普通原子命令竞争默认入口。
|
||||
3. 参数、约束或安全语义不确定时只读 leaf Schema:`dws schema --cli-path "aitable <leaf>" --format json`;只有当前 Cobra flag 不确定时才读对应 `--help`。
|
||||
4. 复杂字段、筛选、导入导出、视图、权限或工作流任务,按“低频能力与 Reference”只加载相关文件,不预读整个 `references/aitable/`。
|
||||
5. 现有骨架和 reference 都无法定位能力时,才用 Runtime Shortcut Catalog 做最后发现;不得猜 `cli_path` 或 flag。
|
||||
6. Schema、Help、reference 与实际返回冲突时采用更安全的解释并报告契约漂移;`confirmation=user_required` 时先确认,再添加 `--yes`。
|
||||
7. 用户已给足目标、字段和数据时,按依赖链连续执行;中间结果只用于提取真实 ID 和判断停止条件,全部完成后统一回读并答复。
|
||||
|
||||
<!-- VISIBLE_SHORTCUTS_START -->
|
||||
## Shortcuts(无专用脚本/recipe 时优先)
|
||||
## Shortcut 发现(按需)
|
||||
|
||||
以下 shortcut 同时进入公开 catalog 与 Runtime Schema。先按本 skill 的意图表、脚本和 recipe 路由:存在精确覆盖该场景的专用脚本/recipe 时按其执行;否则用户意图命中时,shortcut 优先于手写原子命令。命令已选中时直接执行;只在参数或安全语义不确定时读取 leaf Schema(例如 `dws schema --cli-path "aitable +<shortcut>" --format json`),在当前 Cobra flags 不确定时读取 `dws aitable <shortcut> --help`。仅当现有路由和 reference 都无法定位低频能力时,才用 `dws shortcut list --service aitable --format json` 批量发现。
|
||||
`aitable` 当前有 29 条公开 shortcut,完整清单保留在 Runtime Shortcut Catalog,根 Skill 不重复展开。
|
||||
|
||||
| Shortcut | 风险 | 适用场景 |
|
||||
|---|---|---|
|
||||
| `dws aitable +base-get` | read | 获取指定 Base 的目录信息(tables / dashboards summary) |
|
||||
| `dws aitable +base-list` | read | 获取当前用户可访问的 AI 表格 Base 列表(最近访问,支持游标分页) |
|
||||
| `dws aitable +base-search` | read | 按名称关键词搜索 AI 表格 Base |
|
||||
| `dws aitable +chart-get` | read | 获取指定 chart 的详细信息 |
|
||||
| `dws aitable +chart-widgets-example` | read | 获取所有图表类型的 widget config 示例 |
|
||||
| `dws aitable +dashboard-config-example` | read | 获取 dashboard config 的结构示例 |
|
||||
| `dws aitable +dashboard-get` | read | 获取指定 dashboard 的详细信息(含 charts summary) |
|
||||
| `dws aitable +field-get` | read | 批量获取字段详情(含类型相关完整配置) |
|
||||
| `dws aitable +find-record` | read | 在指定多维表里按关键词查记录(只读) |
|
||||
| `dws aitable +form-field-list` | read | 列出表单视图当前可见的字段及其配置 |
|
||||
| `dws aitable +form-list` | read | 列出指定数据表下的所有表单视图 |
|
||||
| `dws aitable +form-share-get` | read | 读取视图当前的分享表单配置 |
|
||||
| `dws aitable +list-tables` | read | 列出某个多维表(base)里的所有数据表(只读,投影 tableId/tableName) |
|
||||
| `dws aitable +record-history-list` | read | 按 recordId 查询单条记录的变更历史 |
|
||||
| `dws aitable +record-query` | read | 查询表格记录(按 ID 取 / 条件筛选 / 关键词 / 分页) |
|
||||
| `dws aitable +record-query-empty` | read | 扫描并过滤出完全没填用户字段的空行 |
|
||||
| `dws aitable +record-share-links` | read | 批量(可 >20 条)获取多维表记录分享链接:去重+分片+合并 |
|
||||
| `dws aitable +record-share-url` | read | 按 recordId 批量获取记录分享链接,单次最多 20 条 |
|
||||
| `dws aitable +resolve-base` | read | 按名称搜索多维表 Base 并解析出唯一 baseId(只读) |
|
||||
| `dws aitable +resolve-table` | read | 在某个多维表 Base 内按名称解析出唯一的数据表 tableId(只读) |
|
||||
| `dws aitable +role-list` | read | 列出指定 Base 下的全部角色 |
|
||||
| `dws aitable +section-list-empty` | read | 列出指定 Base 下所有没有子节点的空文件夹 |
|
||||
| `dws aitable +section-list-nodes` | read | 列出指定 Base 当前版本下的全部 nsheet 节点 |
|
||||
| `dws aitable +table-get` | read | 批量获取指定数据表的表级信息、字段目录与视图目录 |
|
||||
| `dws aitable +template-search` | read | 按名称关键词搜索 AI 表格模板 |
|
||||
| `dws aitable +view-get` | read | 获取视图完整信息(列顺序、筛选、排序、分组等) |
|
||||
| `dws aitable +view-get-frozen-cols` | read | 获取视图当前冻结的左侧列数 |
|
||||
| `dws aitable +view-get-lock` | read | 获取视图锁定状态 |
|
||||
| `dws aitable +view-get-row-height` | read | 获取视图单元格行高(像素) |
|
||||
仅当现有路由和 reference 都无法定位低频能力时,才执行 `dws shortcut list --service aitable --compact --format json` 做最后回退;不要为已知高频意图加载完整 Shortcut Catalog 或产品级 Schema。
|
||||
<!-- VISIBLE_SHORTCUTS_END -->
|
||||
|
||||
## 意图表
|
||||
## 核心对象与 ID
|
||||
|
||||
| 用户说 | 命令 |
|
||||
|--------|------|
|
||||
| "搜表格 / 找一个 base" | `dws aitable base search --query "<名>"` |
|
||||
| "创建 AI 表格 / 多维表" | `dws aitable base create --name "<名称>" [--template-id <id>]` |
|
||||
| "查数据表 / 建数据表" | `dws aitable table get --base-id <baseId>` / `dws aitable table create --base-id <baseId> --name "<表名>" --fields '[...]'` |
|
||||
| "查字段 / 字段类型" | `dws aitable field get --base-id <id> --table-id <id>` |
|
||||
| "查记录 / 搜索记录" | `dws aitable record query --base-id <baseId> --table-id <tableId> [--filters '...']` |
|
||||
| "写记录 / 更新记录 / 删除记录" | `dws aitable record create/update/delete --base-id <baseId> --table-id <tableId> ...` |
|
||||
| "筛选 / 排序 / 公式 / 跨表引用" | 先读 `references/aitable/aitable-filter-sort.md` / `aitable-formula-guide.md` |
|
||||
| "批量导入 JSON / CSV" | `python scripts/import_records.py <baseId> <tableId> data.csv\|data.json` |
|
||||
| "批量加字段" | `python scripts/bulk_add_fields.py --base-id <id> --table-id <id> --fields fields.json` |
|
||||
| "导入 / 导出表格" | 先读 `references/aitable/aitable-export-import.md`;导出优先 `python scripts/aitable_export_via_task.py <baseId> --scope table --table-id <tableId>` |
|
||||
| "仪表盘 / 图表" | 先读 `references/aitable/aitable-dashboard-chart.md` |
|
||||
| "上传附件到记录" | 先读 `references/aitable/aitable-attachment.md`;可用 `python scripts/upload_attachment.py --base-id <id> --file <path>` |
|
||||
| 对象 | 标识与执行边界 |
|
||||
|---|---|
|
||||
| Base | `baseId` 标识一个 AI 表格文件;名称只用于搜索或消歧,不能当 ID |
|
||||
| Table | `tableId` 标识 Base 内的数据表;必须来自 `+resolve-table` / `+table-get` / 创建返回 |
|
||||
| Field | `fieldId` 标识列;写入、筛选、排序和字段变更优先使用真实 `fieldId` |
|
||||
| Record | `recordId` 标识行;更新、删除和分享前必须先查询得到真实 ID |
|
||||
| View / Dashboard / Chart | `viewId` / `dashboardId` / `chartId` 各自绑定当前 Base/Table,不跨对象复用 |
|
||||
| 异步任务 | `taskId` / `importId` 只用于对应导出或导入任务,不能替代业务对象 ID |
|
||||
|
||||
## 标准 SOP(必遵流程)
|
||||
所有下游 ID 都从当前链路的结构化返回中提取;同名多候选必须让用户消歧,不默认取第一项。Base → Table → Field/Record/View 的容器关系必须保持一致,不跨 Base 或 Table 复用子对象 ID。
|
||||
|
||||
> 命中以下意图**必须**按对应 SOP 顺序执行;**禁止**跳步、替换命令、编造 flag/ID。每条命令必须带 `--format json`,执行后必须按"解析"步取真实字段,不得凭返回结构猜测。`baseId`/`tableId`/`fieldId`/`recordId` 一律先查后用,**禁止默认/编造**。
|
||||
创建、复制、导入或新建字段/记录返回 ID 后,立即绑定同一请求中的“这个”“刚才新建的”等指代;除非用户明确转向历史资源,否则不得再按名称搜索并替换为旧对象。
|
||||
|
||||
### SOP-1 定位 Base 与 Table(list / search → table get)
|
||||
## 核心意图与执行骨架
|
||||
|
||||
**触发**:找/打开某张 AI 表格、不知 baseId 或 tableId。
|
||||
| 用户意图 | 首选骨架 | 必须保留的执行边界 |
|
||||
|---|---|---|
|
||||
| 按名称找 Base | `dws aitable +resolve-base --name "<名称>" --format json` | 唯一命中才继续;多候选停止并消歧 |
|
||||
| 浏览最近访问 | `dws aitable +base-list --format json` | 只代表最近访问,不得宣称全量 |
|
||||
| 搜索模板 | `dws aitable template search --query "<关键词>" --format json` | 只返回真实候选,不擅自套用模板或创建 Base |
|
||||
| 按名称找 Table | `dws aitable +resolve-table --base <baseId> --name "<表名>" --format json` | `baseId` 必须来自上一步真实返回 |
|
||||
| 取表、字段与视图目录 | `dws aitable +table-get --base-id <baseId> [--table-ids <tableId>] --format json` | `tables[].fields[]` 是字段目录;完整类型/config 再用 `+field-get` |
|
||||
| 取字段完整配置 | `dws aitable +field-get --base-id <baseId> --table-id <tableId> [--field-ids <ids>] --format json` | 写入前核对类型、只读性和 select options;按需展开以控制返回体 |
|
||||
| 查/搜/筛记录 | `dws aitable +record-query --base-id <baseId> --table-id <tableId> [--query <词>\|--filters '<JSON>'\|--record-ids <ids>] --format json` | ID 模式忽略 filter/sort;全量结论必须完整分页 |
|
||||
| 新增记录 | `dws aitable record create --base-id <baseId> --table-id <tableId> --records '[{"cells":{"<fieldId>":<值>}}]' --format json` | 单次最多 100;取 `data.newRecordIds[]` 后立即按 ID 回读 |
|
||||
| 更新记录 | `dws aitable record update --base-id <baseId> --table-id <tableId> --records '[{"recordId":"<id>","cells":{"<fieldId>":<值>}}]' --format json` | 先 query 拿 recordId;只传需改字段;取 `data.recordIds[]` 后回读 |
|
||||
| 删除记录 | 先 `dws aitable +record-query ...` 定位,再 `dws aitable record delete --base-id <baseId> --table-id <tableId> --record-ids <ids>` | 展示目标与影响,得到明确确认后才加 `--yes` |
|
||||
| 创建 Base / Table | `dws aitable base create --name "<名>"` / `dws aitable table create --base-id <id> --name "<名>" --fields '[...]'` | 使用创建返回的真实 ID;系统改名/加后缀时不得继续猜原名 |
|
||||
| 复制视图 | `dws aitable view duplicate --base-id <baseId> --table-id <tableId> --view-id <viewId> --new-name "<名>"` | `viewId` 必须属于当前表;不能用复制 Table 或新建 Dashboard 替代 |
|
||||
| 批量追加 CSV / JSON 到已有表 | `python3 scripts/import_records.py <baseId> <tableId> <file> [batch_size]` | CSV 表头必须是 fieldId;脚本返回不完整 ledger 时不得宣称全成功 |
|
||||
| 文件导入为新数据表 | `python3 scripts/aitable_import_via_task.py <baseId> <file>` | 与“追加已有 table”不同;走 prepare → PUT → import task |
|
||||
| 批量创建字段 | `python3 scripts/bulk_add_fields.py <baseId> <tableId> fields.json` | 单次最多 15;逐项检查成功/失败结果 |
|
||||
| 导出 Base / Table / View | `python3 scripts/aitable_export_via_task.py <baseId> --scope all\|table\|view [...]` | 保存路径、覆盖与异步未完成状态必须显式处理 |
|
||||
| 上传记录附件 | `python3 scripts/upload_attachment.py <baseId> <file>` | 返回 `fileToken` 后仍需按字段格式写入记录并回读 |
|
||||
|
||||
1. **选源(必须)**:有名称/关键词 → `dws aitable base search --query "<名称>"`;列最近访问 → `dws aitable base list`。`base list` 仅返回最近访问,不是全部,**禁止**当作全量清单。
|
||||
2. **执行(必须)**:`dws aitable base search --query "<完整名>" --format json`(或 `dws aitable base list --format json`)。
|
||||
3. **解析(必须)**:从 JSON 取真实 `baseId`;**多候选必须输出让用户选,禁止默认取第一个**。
|
||||
4. **取 tableId(必须)**:`dws aitable table get --base-id <baseId> --format json` → 从 `data.tables[].tableId` 取目标表 ID,并记录 `views[]`。枚举模式不返回 `fields[]`;需要字段目录时必须继续执行 SOP-2 的 `field get`。若只核对某张表,可显式加 `--table-ids <tableId>` 控制返回体。
|
||||
5. **失败(必须)**:`base list` 为空或不命中 → 换 `base search --query` 关键词重试一次;仍无果**必须如实告知**,禁止臆造 baseId/tableId。
|
||||
## 记录读写不变量
|
||||
|
||||
**禁止**:跳过 `table get` 直接用字段名写记录、用模糊名匹配当 baseId、用旧会话里的 ID 不再校验。
|
||||
- `record create/update` 前必须获取目标字段的 `fieldId`、`type` 与 `config`;`filterUp`、`lookup` 等只读字段不可写。完整格式只在需要时读 [aitable-cell-value.md](references/aitable/aitable-cell-value.md)。
|
||||
- 筛选和排序字段使用 `fieldId`;`--filters` 最外层是 `and|or + operands`,`--sort` 使用 `direction: asc|desc`。日期和跨表字段规则按需读 [aitable-filter-sort.md](references/aitable/aitable-filter-sort.md)。
|
||||
- `record query --all` 仍受 `--page-limit` 约束;分页中断或局部富化失败时保留已有结果,输出 completeness 与逐项失败 ledger,不把部分结果描述为全量。
|
||||
- 创建、更新、导入、批量建字段等写操作必须检查业务 `status`、逐项结果与返回 ID;普通写入按用户明确要求执行后回读,不能只凭退出码宣称成功。
|
||||
- 长 JSON 使用 `--records-file` / 任务文件;不得为绕过字段错误而静默丢列、改类型或删除失败项。
|
||||
|
||||
### SOP-2 拿字段定义(field get,写记录/改字段前置)
|
||||
## 写入计划与验证
|
||||
|
||||
**触发**:建/改/写记录、改字段名或 options、按字段类型拼写入参前。
|
||||
- 用户明确列出的“先创建、再加字段、然后写记录/建视图”等阶段是可观察的验收步骤,必须逐项真实执行;不能为了得到相似终态而折叠、重排或省略。
|
||||
- 删除 Base/Table/Field/Record、关闭高级权限、删除角色和其他高风险动作,先固化目标 ID、所属容器、影响数量、副作用与可恢复性;确认前写调用为零。
|
||||
- 批量导入、建字段和记录写入在第一笔写入前完成全部字段类型、只读性、关联表和文件边界校验;部分失败保留输入顺序与逐项 ledger。
|
||||
|
||||
1. **前置(必须)**:先按 SOP-1 拿到 `baseId` + `tableId`。
|
||||
2. **执行(必须)**:`dws aitable field get --base-id <baseId> --table-id <tableId> --format json`(仅展开需要的字段时加 `--field-ids fld1,fld2`,单次最多 10 个)。
|
||||
3. **解析(必须)**:取每个目标字段的 `fieldId`、`type`、`config`(如 singleSelect/multipleSelect 的 `options[].id|name`);写入 cells 的 key **必须用 `fieldId`**,不是字段中文名;select 字段过滤/写入传**选项名称字面量**,不传 option ID。
|
||||
4. **衔接(必须)**:拿到字段定义 → 进入 SOP-3 写记录、或 `dws aitable field update --field-id <fieldId> --name <新名>|--config <JSON> --format json` 改字段。
|
||||
5. **失败(必须)**:字段不存在或类型不符 → 重新 `field get` 核对,**禁止**凭旧名称/旧类型继续写入。
|
||||
| 写入对象 | 成功后必须验证 |
|
||||
|---|---|
|
||||
| Base / Table | 使用创建返回 ID 查询对象及所属关系 |
|
||||
| Field | `+field-get` 核对 fieldId、type、config 与只读性 |
|
||||
| Record | 使用返回 recordId 按 ID 回读目标 cells |
|
||||
| View / Dashboard / Chart | 重新读取当前 Base/Table 下的对象配置 |
|
||||
| 导入 / 导出任务 | 核对 task 状态、结果对象或输出文件完整性 |
|
||||
|
||||
**禁止**:用字段中文名当 cells key、跳过 `field get` 直接 `record create/update`、对 select 字段传 option ID 当写入值。
|
||||
退出码 0、`status=success`、空对象或仅有 taskId 都不能单独证明业务完成。字段未生效、回读不一致、分页不完整或异步任务未完成时,报告失败、部分完成或进行中,不得声称“全部完成”。
|
||||
|
||||
### SOP-3 写/批量写记录(record create)
|
||||
## 低频能力与 Reference
|
||||
|
||||
**触发**:新增记录、批量加数据、CSV/JSON 入表。
|
||||
| 场景 | 按需读取 |
|
||||
|---|---|
|
||||
| 完整命令索引、对象 URL 与一级路由 | [aitable.md](references/aitable.md) |
|
||||
| 记录 query/create/update/delete/upsert/history/share | 对应 `references/aitable/aitable-record-*.md` |
|
||||
| 字段创建、字段 config、cellValue、公式与跨表引用 | [aitable-field.md](references/aitable/aitable-field.md)、[aitable-field-properties.md](references/aitable/aitable-field-properties.md)、[aitable-cell-value.md](references/aitable/aitable-cell-value.md)、[aitable-formula-guide.md](references/aitable/aitable-formula-guide.md) |
|
||||
| 筛选、排序、统计、全量分析 | [aitable-filter-sort.md](references/aitable/aitable-filter-sort.md)、[aitable-data-analysis-sop.md](references/aitable/aitable-data-analysis-sop.md) |
|
||||
| 导入导出、附件 | [aitable-export-import.md](references/aitable/aitable-export-import.md)、[aitable-attachment.md](references/aitable/aitable-attachment.md) |
|
||||
| 视图、表单、仪表盘与图表 | [aitable-view-config.md](references/aitable/aitable-view-config.md)、[aitable-view-extras.md](references/aitable/aitable-view-extras.md)、[aitable-form.md](references/aitable/aitable-form.md)、[aitable-dashboard-chart.md](references/aitable/aitable-dashboard-chart.md) |
|
||||
| 高级权限、自动化工作流、导航节点 | [aitable-advperm.md](references/aitable/aitable-advperm.md)、[aitable-workflow.md](references/aitable/aitable-workflow.md)、[aitable.md](references/aitable.md) 的 section 路由 |
|
||||
|
||||
1. **前置(必须)**:SOP-1 取 `baseId`/`tableId` + SOP-2 取 `fieldId`/类型。
|
||||
2. **执行(必须)**:`dws aitable record create --base-id <baseId> --table-id <tableId> --records '[{"cells":{"<fieldId>":<值>}}]' --format json`;单次最多 100 条,超长用 `--records-file ./data.json`。
|
||||
3. **写入格式(必须)**:按 `record create --help` 类型表严格传值(text→字符串、number→数值、singleSelect→"选项名"、date→RFC3339、url→`{"text","link"}`、group→`{"cid"}` 等);`filterUp`/`lookup` 字段只读不可写。
|
||||
4. **解析与验证(必须)**:从返回 `data.newRecordIds[]` 取全部新记录 ID;不要读取不存在的标量 `recordId`。立即执行 `dws aitable record query --base-id <baseId> --table-id <tableId> --record-ids <id1,id2,...> --format json` 回读写入值。
|
||||
5. **失败(必须)**:类型/格式错误按返回报错修正后重试,**禁止**降级丢弃字段;不确定格式先 `field get` 复核 config。
|
||||
## 错误恢复
|
||||
|
||||
**禁止**:编造 fieldId/recordId、跳过 `field get` 凭中文名写、把 URL 字符串直接塞给 url 字段。
|
||||
|
||||
### SOP-4 查/筛/排记录(record query)
|
||||
|
||||
**触发**:查记录、按条件筛选、排序、取关联记录、定位待改/待删的 recordId。
|
||||
|
||||
1. **前置(必须)**:SOP-1 拿 `baseId`/`tableId`。
|
||||
2. **执行(必须)**:`dws aitable record query --base-id <baseId> --table-id <tableId> --format json`;已知 ID 直取加 `--record-ids rec1,rec2`(忽略 filters/sort,单次≤100)。
|
||||
3. **筛选/排序(必须)**:`--filters` 最外层必须 `{"operator":"and|or","operands":[...]}`,select 字段值传**选项名字面量**;日期只能用 `date_eq/before/after/not_before/not_after`,范围用 `not_before`+`not_after` 组合,**禁止** `eq`/区间/相对时间。`--sort` 用 `[{"fieldId":"..","direction":"asc|desc"}]`(**必须用 `direction`**)。公式/引用/关联字段默认不返回,需显式 `--field-ids` 指定。
|
||||
4. **解析(必须)**:取真实 `recordId` 与字段值;分页用 `--cursor`,全表用 `--all --page-limit N`。
|
||||
5. **衔接(必须)**:拿到 recordId → SOP-5 更新、`record delete --record-ids --yes` 删除(删前确认)。
|
||||
|
||||
**禁止**:用字段名做 filter/sort key、对日期用 `eq`、漏掉 `direction` 用旧 `order` 字段、用本地过滤替代服务端 filter。
|
||||
|
||||
### SOP-5 更新记录(record update)
|
||||
|
||||
**触发**:改记录字段值、批量更新状态、单字段重命名需求之外的记录改动。
|
||||
|
||||
1. **前置(必须)**:SOP-1 拿 `baseId`/`tableId`;SOP-2 拿字段类型;SOP-4 拿目标 `recordId`。
|
||||
2. **执行(必须)**:`dws aitable record update --base-id <baseId> --table-id <tableId> --records '[{"recordId":"recXXX","cells":{"<fieldId>":<新值>}}]' --format json`(每条必含 `recordId`+`cells`,单次≤100;超长用 `--records-file`);只传需改字段,未传保持原值。
|
||||
3. **解析与验证(必须)**:写入格式同 SOP-3;从返回 `data.recordIds[]` 取实际更新的记录 ID。更新响应不返回“受影响字段”,必须立即用 `record query --record-ids <id1,id2,...> --format json` 回读目标字段确认。
|
||||
4. **失败(必须)**:recordId 不存在或类型不符 → 回 SOP-4 重新定位,**禁止**编造 ID 强写。
|
||||
|
||||
**禁止**:省略 `recordId`、用字段中文名当 cells key、凭空猜测 recordId 直接 update。
|
||||
|
||||
## 危险操作
|
||||
|
||||
`base delete` / `table delete` / `field delete` / `record delete` 不可逆,必须先向用户确认再加 `--yes`。
|
||||
|
||||
## 高频硬约束
|
||||
|
||||
- 创建/改字段/写记录是多轮连续任务时,不能在"让我执行/先获取 ID"后停下;必须实际调用对应 `dws aitable` 命令并验证结果。
|
||||
- 字段重命名使用 `dws aitable field update --base-id <baseId> --table-id <tableId> --field-id <fieldId> --name "<新名称>" --format json`;先 `field get` 找真实 `fieldId`,不要猜字段名能直接更新。
|
||||
- 写记录前必须 `field get` 获取 `fieldId` 与类型;`record create/update` 的 `cells` key 用 `fieldId`,不是字段中文名。长 JSON 使用 `--records-file`。
|
||||
- 表或字段创建返回名称被系统自动加后缀时,后续必须使用返回的真实 `tableId`/`fieldId`,不要继续按原名称猜。
|
||||
- `record update/delete` 先 `record query/list` 定位 `recordId`;删除必须确认,普通新增/更新按用户明确要求可直接执行后读回验证。
|
||||
- `record query/create/update/delete`、`field create`、导入导出、图表和附件场景必须先读对应 `references/aitable/*.md`,不要凭旧单文件参数猜 flag。
|
||||
|
||||
## 字段类型规则
|
||||
|
||||
详见本 skill 的 [field-rules.md](references/field-rules.md)。
|
||||
- 路径或 flag 错误:按既定的 leaf Schema → leaf Help 顺序校正一次;仍失败则停止,不连续尝试猜测别名。
|
||||
- 命令非零、输出非 JSON、业务 `status != success`、必需 ID 缺失、批处理部分失败均视为失败;保留成功项与 ledger,禁止吞错。
|
||||
- 同名歧义、权限不足、资源不存在、字段类型漂移、分页无法推进或 Schema/Help 冲突时停止并报告。具体恢复动作按需读 [aitable-error-recovery.md](references/aitable/aitable-error-recovery.md)。
|
||||
- 已确认远端未写入且契约声明可重试时,才从最新实际输出重新提取 ID 后重试;写入状态未知、删除和其他 `confirmation=user_required` 操作不得自动重试或静默确认。
|
||||
|
||||
## 跨产品协作
|
||||
|
||||
- 单元格 / 工作表 / 公式 → 切到 `dingtalk-misc`(`references/sheet.md`,命令前缀:`dws sheet`)
|
||||
## 局部意图
|
||||
|
||||
- [局部意图消歧](references/intent-guide.md)。
|
||||
- 电子表格工作表、单元格与公式 → `dingtalk-sheet`;结构化 Base/Table/Field/Record 才走本 skill。
|
||||
- 普通文档内容 → `dingtalk-doc`;钉盘普通文件与文件夹 → `dingtalk-drive`;记录附件上传仍走本 skill。
|
||||
- 用户直接提供类型不明的 alidocs URL 时,按 `dws-shared` 的 URL 预检导航确认 `extension=able` 后再执行。
|
||||
- 听记内容入表:先用 `dingtalk-minutes` 提取结构化结果,再按本 skill 的字段与记录规则写入。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 数据分析
|
||||
|
||||
> 定位:仅在用户要求跨记录统计、聚合或业务结论时加载。普通查找、筛选和分页先按根 Skill 的 `+record-query` 骨架执行;完整分析契约以 [aitable-data-analysis-sop.md](aitable/aitable-data-analysis-sop.md) 为准,本页只保留轻量入口。
|
||||
|
||||
> 本场景所有 recipe 均为 full。
|
||||
|
||||
| Recipe | 行动指南(固定路线) |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 业务域通用规范
|
||||
|
||||
> 定位:供 AITable 主题 Reference 复用的批量、并行采集和 ID 传递约定,不是用户意图路由入口。根 Skill 已明确的高频任务不需要单独加载本页。
|
||||
|
||||
> 仅服务本 skill 已迁入的行动指南。安全门控、危险操作确认、`--format json` 等已在本 skill 的 `SKILL.md` 中定义,此处不重复。
|
||||
|
||||
## 批量查询规范
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 记录操作详细指南
|
||||
|
||||
> 定位:兼容性的记录操作总览。新任务优先按需读取 `aitable/aitable-record-{query,create,update,delete,upsert}.md` 中唯一对应的叶级 Reference;不要同时加载本页和全部叶级文件。本页不得作为另一套命令发现源。
|
||||
|
||||
## 查询记录
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
# AI表格 (aitable) 命令参考
|
||||
|
||||
> **渐进式文档**:本文件为路由层(索引 + 意图判断),各命令的详细参数、示例和踩坑说明在 [aitable/](./aitable/) 目录下按需加载。
|
||||
> 定位:低频 atomic 能力的一级索引,不是每次调用必读的产品手册。已知高频意图直接回到 [`SKILL.md`](../SKILL.md);参数和安全语义查 leaf Schema,真实 flags 查 leaf Help;本页只在根路由无法定位能力时用于选择一个主题 Reference,禁止递归加载全部链接。
|
||||
|
||||
## 使用本索引
|
||||
|
||||
1. 先按对象选择一个分支:Base/Table、Field、Record、View/Form、Dashboard/Chart、Workflow、Advperm、Import/Export 或 Section。
|
||||
2. 只打开该分支链接的一个主题文件;命令已确定后停止发现。
|
||||
3. 所有子对象 ID 必须来自当前 Base/Table 链路;写操作完成后按对象回读。
|
||||
4. 本页示例是导航证据,不覆盖 Runtime/Cobra、leaf Schema 或根 Skill 的 Golden Route。
|
||||
|
||||
各命令的详细参数、示例和边界在 [aitable/](./aitable/) 目录下按需加载。
|
||||
|
||||
## 文档地址 (URI)
|
||||
|
||||
@@ -35,7 +44,7 @@
|
||||
|
||||
| 命令 | 用途 | 必填参数 | 路由提醒 |
|
||||
|------|------|----------|----------|
|
||||
| `table get` | 获取数据表/视图目录 | `--base-id` | 不传 `--table-ids` 枚举全部表,但不返回字段;字段目录使用 `field get` |
|
||||
| `table get` | 获取表级信息、字段目录与视图目录 | `--base-id` | 不传 `--table-ids` 枚举全部表并返回精简 `fields[]`/`views[]`;字段完整 config 使用 `field get` |
|
||||
| `table create` | 创建数据表 | `--base-id` `--name` `--fields` | fields 为 JSON 数组,至少 1 个 |
|
||||
| `table update` | 修改表名 / 备注 / 行命名规则 | `--base-id` `--table-id` + 三选一(`--name` / `--description` / `--record-name-key`) | `--record-name-key` 是固定枚举(如 task/project/event/customer/ji_lu 等),非字段 ID |
|
||||
| `table delete` | 删除表 | `--base-id` `--table-id` | 不可逆 |
|
||||
@@ -329,160 +338,3 @@ dws aitable export data --base-id <BASE_ID> --task-id <TASK_ID> --timeout-ms 300
|
||||
- `scope=all`:只需 `base-id`
|
||||
- `scope=table`:必须 `table-id`
|
||||
- `scope=view`:必须同时 `table-id + view-id`
|
||||
|
||||
## 意图判断
|
||||
|
||||
用户说"表格/多维表/AI表格":
|
||||
- 查看/查找/列表 → `base search`(优先)或 `base list`(仅浏览最近访问)
|
||||
- 详情 → `base get`
|
||||
- 创建 → `base create`
|
||||
- 修改 → `base update`
|
||||
- 删除 → `base delete`
|
||||
|
||||
用户说"数据表/子表/table":
|
||||
- 查看 → `table get`
|
||||
- 创建 → `table create`
|
||||
- 重命名 / 改备注 / 改行命名规则 → `table update`(三选一:`--name` / `--description` / `--record-name-key`)
|
||||
- 用户说"行命名规则/记录别名/卡片显示成 task/project/event 这种" → `table update --record-name-key <枚举键>`,**中文 → 枚举键**对照见 [aitable-record-name-key.md](./aitable/aitable-record-name-key.md)
|
||||
- 删除 → `table delete`
|
||||
|
||||
用户说"字段/列/column":
|
||||
- 查看 → `field get`
|
||||
- 添加 → `field create`(读 [aitable-field.md](./aitable/aitable-field.md))
|
||||
- 修改 → `field update`
|
||||
- 删除 → `field delete`
|
||||
|
||||
用户说"记录/行/数据/row":
|
||||
- 查看/搜索 → `record query`(读 [aitable-record-query.md](./aitable/aitable-record-query.md))
|
||||
- 找空行 / 没填东西的行 → `record query-empty`(读 [aitable-record-query.md](./aitable/aitable-record-query.md))
|
||||
- 已知 recordId 反查字段值 → `record get`(按 ID 取专用,等价 `record query --record-ids`)
|
||||
- 添加/写入 → `record create`(读 [aitable-record-create.md](./aitable/aitable-record-create.md))
|
||||
- 修改/更新(每条独立 cells) → `record update`(读 [aitable-record-update.md](./aitable/aitable-record-update.md))
|
||||
- **批量更新同一字段值**(统一标记/统一改值) → `record batch-update --record-ids ... --cells '{...}'`
|
||||
- 删除 → `record delete`
|
||||
- **查记录的字段变更历史 / 操作审计** → `record history-list`(读 [aitable-record-history.md](./aitable/aitable-record-history.md))
|
||||
- **取记录分享链接 / 把这行发给同事** → `record share-url`(读 [aitable-record-share.md](./aitable/aitable-record-share.md))
|
||||
- **不知道有没有 → 有就改、没有就建** → `record upsert`(读 [aitable-record-upsert.md](./aitable/aitable-record-upsert.md))
|
||||
|
||||
用户说"视图/view":
|
||||
- 列出/查看全部视图 → `view list`(或 `view get` 不传 --view-ids,二者等价)
|
||||
- 看某个视图详情 → `view get --view-ids <ID>`
|
||||
- 创建 → `view create`
|
||||
- 修改(含"调整字段顺序/隐藏字段") → `view update --config '{"visibleFieldIds":[...]}'`
|
||||
- 修改某一项配置(filter/sort/group/card/timebar/aggregate 等)→ `view update <attr>`(读 [aitable-view-config.md](./aitable/aitable-view-config.md))
|
||||
- 锁定 / 冻结列 / 行高 / 数据高亮规则 / 复制视图 → 读 [aitable-view-extras.md](./aitable/aitable-view-extras.md)
|
||||
- 删除 → `view delete`
|
||||
|
||||
用户说"锁定视图/解锁视图/lock view" → `view lock` / `view lock --off`,详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md)
|
||||
|
||||
用户说"冻结列/冻结首列/frozen columns" → `view update frozen-cols --count N`,详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md)
|
||||
|
||||
用户说"行高/单元格高度/紧凑模式/cell height" → `view update row-height --cell-height N`(合法档位 32/56/88/128),详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md)
|
||||
|
||||
用户说"数据高亮/条件格式/单元格上色/fill color rule" → `view update fill-color-rule --json '[...]'`,详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md)
|
||||
|
||||
用户说"复制视图/duplicate view" → `view duplicate --view-id ... [--new-name ...]`,详见 [aitable-view-extras.md](./aitable/aitable-view-extras.md)
|
||||
|
||||
用户说"筛选/过滤/filter" → 读 [aitable-filter-sort.md](./aitable/aitable-filter-sort.md)
|
||||
|
||||
用户说"统计/分析/聚合/TOP N/全量" → 读 [aitable-data-analysis-sop.md](./aitable/aitable-data-analysis-sop.md)
|
||||
|
||||
用户说"公式/formula/计算字段/派生指标" → 读 [aitable-formula-guide.md](./aitable/aitable-formula-guide.md)
|
||||
|
||||
用户说"查找引用/lookup/filterUp/跨表" → 读 [aitable-formula-guide.md](./aitable/aitable-formula-guide.md)(§5.4 跨表引用)
|
||||
|
||||
用户说"表单/form/收集表/问卷/催办填写" → 读 [aitable-form.md](./aitable/aitable-form.md)
|
||||
|
||||
用户说"自动化/工作流/流程/触发/automation/workflow" → 读 [aitable-workflow.md](./aitable/aitable-workflow.md)
|
||||
- 新建自动化 → 按子文档的最小 Demo 组装完整 DSL,再 `workflow create --dsl @file`
|
||||
- 修改自动化 → `workflow get` 留底,按最新 DSL 文档生成完整目标 DSL,再 `workflow update --dsl @file`
|
||||
- 看 Base 里有哪些流程 / 哪些在跑 → `workflow list`(看 `recordCount` / `runningCount`)
|
||||
- 看某个流程具体配置(触发条件、动作步骤) → `workflow get`
|
||||
- 启用流程 → `workflow enable`
|
||||
- 临时停掉流程(调试 / 数据迁移)→ `workflow disable --yes`
|
||||
- 删除流程:当前不支持,引导用户到 AI 表格 Web 端 → 数据表 → 自动化 面板手动完成
|
||||
|
||||
用户说"仪表盘/图表/chart" → 读 [aitable-dashboard-chart.md](./aitable/aitable-dashboard-chart.md)
|
||||
|
||||
用户说"仪表盘排版乱了/图表对不齐/重新排布/自动布局/美化仪表盘" → `dashboard arrange`(读 [aitable-dashboard-chart.md](./aitable/aitable-dashboard-chart.md))
|
||||
|
||||
用户说"附件/上传文件" → 读 [aitable-attachment.md](./aitable/aitable-attachment.md)
|
||||
|
||||
用户说"导入/导出/import/export" → 读 [aitable-export-import.md](./aitable/aitable-export-import.md)
|
||||
|
||||
用户说"模板" → `template search`
|
||||
|
||||
用户说"高级权限/角色/权限控制/谁能看/谁能改" → 读 [aitable-advperm.md](./aitable/aitable-advperm.md)
|
||||
- 开/关高级权限 → `advperm enable` / `advperm disable --yes`
|
||||
- 看角色配置 → `advperm role-list` 或 `advperm role-get`
|
||||
- 建角色(可同时指定子角色权限) → `advperm role-create --name ... --sub-roles '[...]'`
|
||||
- 改角色名 / 改子角色权限(PATCH 语义,未传字段不变) → `advperm role-update --role-id ... [--name ...] [--sub-roles '[...]']`
|
||||
- 删角色 → `advperm role-delete --yes`
|
||||
- **角色 ↔ 成员绑定**:当前 CLI 不支持,仍需在 AI 表格 Web 端面板手动完成
|
||||
|
||||
命令报错/操作失败 → 读 [aitable-error-recovery.md](./aitable/aitable-error-recovery.md)
|
||||
|
||||
**关键区分**: base=表格文件, table=数据表, field=列, record=行
|
||||
|
||||
## 核心工作流
|
||||
|
||||
```bash
|
||||
# 1. 搜索/列出 Base — 提取 baseId
|
||||
dws aitable base search --query "项目" --format json
|
||||
|
||||
# 2. 获取 Base 信息 — 提取 tableId
|
||||
dws aitable base get --base-id <BASE_ID> --format json
|
||||
|
||||
# 3. 获取字段目录 — 提取 fieldId
|
||||
dws aitable field get --base-id <BASE_ID> --table-id <TABLE_ID> --format json
|
||||
|
||||
# 4. 查询记录
|
||||
dws aitable record query --base-id <BASE_ID> --table-id <TABLE_ID> --format json
|
||||
|
||||
# 5. 新增记录 (cells 用 fieldId 作 key)
|
||||
dws aitable record create --base-id <BASE_ID> --table-id <TABLE_ID> \
|
||||
--records '[{"cells":{"fldXXX":"值"}}]' --format json
|
||||
```
|
||||
|
||||
## 上下文传递表
|
||||
|
||||
| 操作 | 从返回中提取 | 用于 |
|
||||
|------|-------------|------|
|
||||
| `base list/search` | `baseId` | 所有后续命令的 --base-id,拼接文档 URI |
|
||||
| `base create` | `baseId` | 后续命令 + 文档 URI |
|
||||
| `base get` | `tables[].tableId` | --table-id,拼接指定数据表 URI |
|
||||
| `table create` | `tableId` | 后续命令 + 拼接指定数据表 URI |
|
||||
| `table get` | `tables[].tableId`、视图目录 | 定位数据表和视图;字段需继续调用 `field get` |
|
||||
| `field get` | `fields[].fieldId` | record 操作的 cells key, field update/delete |
|
||||
| `record query` | `recordId` | record update/delete;按 ID 反查字段值用 `record get` |
|
||||
| `template search` | `templateId` | base create --template-id,拼接模板预览 URI |
|
||||
|
||||
## URL → baseId 提取
|
||||
|
||||
用户提供 `https://alidocs.dingtalk.com/i/nodes/{baseId}` 链接时:
|
||||
1. 提取 `/nodes/` 后的路径段作为 `baseId`
|
||||
2. 去掉尾部的查询参数(`?` 及其后内容)
|
||||
3. 传入 `--base-id` 参数
|
||||
|
||||
> 如果该 URL 来自 `dws aitable` 返回或已在当前链路 probe 过,可直接复用;
|
||||
> 如果是用户直接提供的原始 `alidocs` URL,则先按 [链接规范](url-patterns.md#alidocs-url-类型探测流程) probe,确认 `extension=able` 后再继续。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 所有操作使用 ID(baseId/tableId/fieldId/recordId),不使用名称
|
||||
- records 的 cells key 是 fieldId,不是字段名称
|
||||
- cells 写入/读取格式见 [aitable-cell-value.md](./aitable/aitable-cell-value.md)
|
||||
- 最佳实践见 [aitable-best-practices.md](./aitable/aitable-best-practices.md)
|
||||
|
||||
## 自动化脚本
|
||||
|
||||
| 脚本 | 场景 |
|
||||
|------|------|
|
||||
| [bulk_add_fields.py](../scripts/bulk_add_fields.py) | 批量添加字段 |
|
||||
| [import_records.py](../scripts/import_records.py) | 从 JSON/CSV 批量导入记录 |
|
||||
| [aitable_export_via_task.py](../scripts/aitable_export_via_task.py) | 文件导出(export_data 轮询 + 下载) |
|
||||
| [upload_attachment.py](../scripts/upload_attachment.py) | 上传附件到 AI 表格记录 |
|
||||
|
||||
## 相关产品
|
||||
|
||||
- [doc](../../dingtalk-doc/references/doc.md) — 富文本文档编辑,不是结构化数据表格
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# advperm — 高级权限管理
|
||||
|
||||
> 加载边界:仅在开启/关闭高级权限或管理自定义角色时读取。普通协作者、文件权限或成员管理不走本页。`baseId` 和 `roleId` 必须来自当前链路;disable、role-delete 等需确认的动作先读现状、固化影响,再执行并回读。
|
||||
|
||||
控制 Base 的高级权限总开关,并管理自定义角色(增删改查 + 子角色权限规则)。
|
||||
适用场景:"如何控制谁能看/改 Base 数据"、"开启/关闭高级权限"、"新建/修改/删除角色"、"按字段或行配置权限"。
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# attachment — 附件上传
|
||||
|
||||
> 加载边界:仅在文件要写入 AITable 的 attachment 字段时读取;普通钉盘上传走 Drive。流程必须完成“申请凭证 → HTTPS PUT → 用 fileToken 写记录 → 按 recordId 回读”,拿到 fileToken 不等于附件已进入记录。
|
||||
|
||||
> **STOP — 不要使用钉盘 (drive) 上传!** 钉盘 fileId 无法写入 attachment 字段。必须使用以下流程。
|
||||
>
|
||||
> **STOP — 严禁在 record create/update 的 cells 里直接传图片 URL!** 直传 `{"url":"https://..."}` 会导致服务端同步下载图片,批量写入时触发 TIMEOUT_ERROR。正确做法:先 `attachment upload` 获取 `fileToken`,再用 `{"fileToken":"ft_xxx"}` 写入。
|
||||
@@ -32,6 +34,7 @@ dws aitable record create --base-id <BASE_ID> --table-id <TABLE_ID> \
|
||||
```
|
||||
|
||||
> `uploadUrl` 有时效性(`expiresAt`),脚本会自动在获取后立即上传。
|
||||
> 脚本只接受成功的业务响应和 HTTPS `uploadUrl`;它返回 `fileToken` 不等于记录已经写入,后续仍需执行 record create/update 并按 recordId 回读。
|
||||
|
||||
## 手动流程(不使用脚本)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# AI 表格最佳实践
|
||||
|
||||
> 定位:跨主题的不变量摘要,用于复杂任务复核,不作为命令索引。若根 Skill 已给出明确骨架,不要为了普通读写预加载本页;具体字段值、过滤或错误恢复分别读取对应专题。
|
||||
|
||||
## 1. 字段可写性分类
|
||||
|
||||
| 字段类型 | 可写 | 正确方式 |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# cells 写入/读取格式规范(cellValue 数据结构)
|
||||
|
||||
> 加载边界:仅在已经选定 record create/update/upsert 且需要构造某种字段值时读取。先用 field get 确认 `fieldId`、`type`、`config` 和可写性;本页不负责找 Base/Table/Record,也不能把字段名当 fieldId。
|
||||
|
||||
> 适用命令:`dws aitable record create --records`、`dws aitable record update --records`、`dws aitable record query` 返回
|
||||
>
|
||||
> 本文件是 DWS AI 表格 cellValue 的 **source of truth**。写入记录时,必须严格按此格式构造 cells 对象。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# dashboard & chart — 仪表盘与图表
|
||||
|
||||
> 加载边界:仅在用户明确操作仪表盘或图表时读取。先用当前 Base 的 dashboard list/get 获取 `dashboardId`,再获取其 `chartId`;不要用 View 或 Table 命令替代,也不要跨 Dashboard 复用 chartId。
|
||||
|
||||
## 建议操作顺序
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# AI 表格数据分析 SOP
|
||||
|
||||
> 加载边界:用于需要全表统计、分组、排名或业务结论的任务。普通记录定位只读 record-query 专题。任何“全部/最高/总数”结论都必须携带分页完整性;未拉全时只能报告当前范围和 continuation。
|
||||
|
||||
> 当用户诉求涉及查询、筛选、排序、统计、Top/Bottom N、分组聚合、判断全局结论时,必须先读本文档再执行。
|
||||
|
||||
## 1. 查询决策树
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# AI 表格错误恢复指南
|
||||
|
||||
> 加载边界:仅在真实命令或业务返回失败后读取,不做预防性全量加载。先保留原错误、阶段、对象 ID 和已成功 ledger;只有能证明未写入且契约允许时才重试,状态未知和需确认写操作禁止自动重放。
|
||||
|
||||
> 当 CLI 命令返回错误时,按本文档的映射表判断恢复动作。
|
||||
|
||||
## 1. 错误响应结构
|
||||
@@ -15,6 +17,7 @@
|
||||
- `status: "error"` 表示操作失败
|
||||
- `summary` 包含错误摘要信息
|
||||
- `trace_id` 用于问题追踪
|
||||
- 命令退出码为 0 但 `status != "success"`、必需 ID 缺失或逐项结果含失败,也属于业务失败
|
||||
|
||||
## 2. 常见错误与恢复动作
|
||||
|
||||
@@ -42,7 +45,7 @@
|
||||
|-------------------|------|---------|
|
||||
| `base not found` | base-id 错误或无权限 | 确认 base-id 正确;尝试 `base list` 或 `base search` 重新定位 |
|
||||
| `table not found` | table-id 错误 | 用 `table get --base-id <baseId>` 不带 table-ids 查看所有表 |
|
||||
| 表名重复 | 同 Base 下已存在同名表 | 系统会自动续号(如"原名 1"),无需额外处理 |
|
||||
| 表名重复 | 同 Base 下已存在同名表 | 使用创建返回的真实 `tableId/tableName`;禁止继续按原名猜测目标 |
|
||||
|
||||
### 2.4 视图操作错误
|
||||
|
||||
@@ -57,7 +60,7 @@
|
||||
|-------------------|------|---------|
|
||||
| filters 无效被忽略 | 根节点不是 and/or,或 operands 格式错误 | 确保 filters 根节点是 `{"operator":"and"/"or", "operands":[...]}` 结构 |
|
||||
| sort 无效 | fieldId 不存在 | 先 `field get` 确认字段 ID |
|
||||
| 筛选结果为空 | 条件过严或字段值不匹配 | 放宽条件验证;注意 singleSelect 筛选值用 option name 或 id |
|
||||
| 筛选结果为空 | 条件过严或字段值不匹配 | 放宽条件验证;singleSelect/multipleSelect 筛选值使用 option name 字面量 |
|
||||
|
||||
### 2.6 导入导出错误
|
||||
|
||||
@@ -65,6 +68,7 @@
|
||||
|-------------------|------|---------|
|
||||
| 导出任务超时 | 数据量大,异步任务未完成 | 用 `export data --task-id <taskId>` 轮询直到完成 |
|
||||
| 导入文件格式错误 | 不支持的文件格式或文件损坏 | 确认文件为 .xlsx 格式且未加密 |
|
||||
| 批处理只成功一部分 | 某批或某个字段返回失败 | 保留成功 ID,输出失败 batch/item ledger,以非零状态结束;不要整批重放 |
|
||||
|
||||
## 3. 重试策略
|
||||
|
||||
@@ -106,18 +110,6 @@ dws aitable record create \
|
||||
|
||||
`--verbose` 会输出请求/响应的详细信息,帮助定位问题。
|
||||
|
||||
### 4.2 使用 --dry-run 预览
|
||||
|
||||
```bash
|
||||
dws aitable record create \
|
||||
--base-id <baseId> \
|
||||
--table-id <tableId> \
|
||||
--records '[...]' \
|
||||
--dry-run --format json
|
||||
```
|
||||
|
||||
`--dry-run` 只预览不执行,适合在不确定参数是否正确时先验证。
|
||||
|
||||
## 5. 错误预防最佳实践
|
||||
|
||||
1. **写记录前先读字段结构** — `field get` 确认字段类型和 ID
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
# export & import — 导入导出
|
||||
|
||||
> 加载边界:仅在文件与 AITable 之间迁移数据时读取。“追加到已有 Table”与“导入成新 Table”必须先分流。`taskId/importId` 只用于对应任务状态,完成后必须验证新 tableId 或本地输出文件;任务已创建不等于业务完成。
|
||||
|
||||
## 导出数据(两阶段轮询)
|
||||
|
||||
`export data` 为异步任务:首次调用可能只返回 `taskId`,需要继续轮询。
|
||||
|
||||
优先使用 `python3 scripts/aitable_export_via_task.py <baseId> --scope all|table|view [...]`:它检查业务状态、持续轮询、要求 HTTPS 下载地址,并在本地文件已存在时停止而不是静默覆盖。只有需要控制底层轮询参数时才走下面的原子命令。
|
||||
|
||||
> ⚠️ **`--format` 冲突警告**:`export data` 的 `--format` 是**导出格式**(excel/attachment 等),不是全局输出格式。**此命令禁止追加全局 `--format json`**,否则会覆盖导出格式导致 `INVALID_EXPORT_FORMAT` 错误。输出默认就是 JSON,无需额外指定。
|
||||
|
||||
```bash
|
||||
@@ -28,6 +32,8 @@ dws aitable export data --base-id <BASE_ID> --task-id <TASK_ID> --timeout-ms 300
|
||||
|
||||
> **无需手动解析 CSV/Excel 再逐条 record create**,效率极低且容易出错。
|
||||
|
||||
新建数据表导入优先使用 `python3 scripts/aitable_import_via_task.py <baseId> <file>`,脚本封装 prepare → PUT → import 并检查每一步业务状态。追加到已有表且需要字段级类型控制时,使用 `python3 scripts/import_records.py <baseId> <tableId> <file> [batch_size]`;二者语义不同,不要自动互换。
|
||||
|
||||
```bash
|
||||
# 第 1 步:申请上传凭证
|
||||
dws aitable import upload --base-id <BASE_ID> \
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 字段类型 config 规范(field create / table create / field update)
|
||||
|
||||
> 加载边界:仅在创建/更新字段或建表时构造字段 `config`。先确认字段类型及当前 Table;记录 cellValue 结构读取 [aitable-cell-value.md](aitable-cell-value.md),公式正文读取 [aitable-formula-guide.md](aitable-formula-guide.md),不要在本页寻找记录写入路由。
|
||||
|
||||
> 适用命令:`dws aitable field create`、`dws aitable table create --fields`、`dws aitable field update --config`
|
||||
>
|
||||
> 本文件是 DWS AI 表格字段 config 的 **source of truth**。创建/更新字段时,必须严格按此规范构造 JSON。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# field — 字段管理
|
||||
|
||||
> 加载边界:仅在字段目录不足、需要完整配置或执行字段 CRUD 时读取。`baseId/tableId` 先由根 Skill 解析;写前校验类型、config、关联表和只读性,写后用 field get 回读。删除字段先固化影响并确认。
|
||||
|
||||
## field get — 获取字段详情
|
||||
|
||||
```
|
||||
@@ -14,7 +16,7 @@ Flags:
|
||||
--table-id string Table ID (必填)
|
||||
```
|
||||
|
||||
返回字段的完整配置(含 options 等)。不要假设未指定 `--table-ids` 的 `table get` 枚举结果含字段;字段目录和配置以 `field get` 返回为准。
|
||||
返回字段的完整配置(含 options 等)。`table get` 会返回精简字段目录,但写入、筛选或修改前需要类型相关完整 `config` 时,以 `field get` 返回为准。
|
||||
|
||||
## field create — 创建字段
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# filters & sort — 筛选排序语法参考
|
||||
|
||||
> 加载边界:仅在 record query 或 view 配置需要构造 filter/sort JSON 时读取。先用 field get 取得真实 fieldId 和类型;服务端可过滤时不先拉全量,本页语法不能替代分页完整性判断。
|
||||
|
||||
> 视图(view)配置的 filter/sort/group **整体写入**请优先用 `view update filter` / `view update sort` / `view update group` 子命令,详见 [aitable-view-config.md](./aitable-view-config.md)。本文件聚焦于 `record query --filters` 与 view config filter 的语法和差异。
|
||||
|
||||
## filters 结构规范
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# form — 表单管理
|
||||
|
||||
> 加载边界:仅在用户明确创建、配置或分享表单视图时读取。先绑定当前 Base/Table,再从创建/list 返回取得 viewId 和 fieldId;表单分享不等于记录分享,也不等于开放 Base 权限。
|
||||
|
||||
## 命令一览
|
||||
|
||||
| 命令 | 用途 |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# AI 表格公式字段指南
|
||||
|
||||
> 加载边界:仅在创建或修改 formula 字段时读取。公式字段是只读派生字段,不能通过 record create/update 写值;字段引用必须基于当前 Table 的精确名称/ID,跨表取值应先分流到 lookup/filterUp。
|
||||
|
||||
> 当用户要创建 formula 类型字段、编写表内计算公式、做派生指标时,必须先读本文档。
|
||||
|
||||
## 1. 何时使用 formula 字段
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 主键文档管理
|
||||
|
||||
> 加载边界:仅在记录的 primaryDoc 字段需要查询或创建关联文档时读取。先取得真实 baseId/tableId/recordId/fieldId;创建返回 nodeId 后,文档正文交给 Doc Skill,并持续使用该 nodeId,不再按标题搜索。
|
||||
|
||||
## 适用场景
|
||||
|
||||
当需要为 AI 表格中的记录创建或查询关联的主键文档时使用。主键文档是 primaryDoc 类型字段对应的钉钉在线文档,可通过 `dws doc` 进行内容读写。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# record create — 新增记录
|
||||
|
||||
> 加载边界:仅在目标 Base/Table 已唯一确定且字段目录已取得后读取。第一笔写入前校验全部 cells;创建返回 `newRecordIds[]` 后按 ID 回读。不得因部分字段失败而静默丢列或改类型。
|
||||
|
||||
## 命令格式
|
||||
|
||||
```
|
||||
@@ -47,6 +49,8 @@ dws aitable record query --base-id <BASE_ID> --table-id <TABLE_ID> \
|
||||
|
||||
创建成功以 `data.newRecordIds[]` 为 ID 来源;不要把整个 `data` 当作单个 recordId,也不要只以退出码作为写入成功证据。
|
||||
|
||||
批量追加本地 JSON/CSV 到已有表时可使用 `python3 scripts/import_records.py <baseId> <tableId> <file> [batch_size]`。脚本会逐批检查业务状态、收集 `newRecordIds` 并按 ID 回读;任何批次失败或回读不完整都会输出 ledger 并以非零状态结束,已有成功批次仍会保留在结果中。
|
||||
|
||||
## cells 写入格式
|
||||
|
||||
各字段类型的写入格式见 [aitable-cell-value.md](./aitable-cell-value.md)。
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# record delete — 删除记录
|
||||
|
||||
> 加载边界:仅在用户明确要求删除记录时读取。先 query 并展示真实 recordId、关键字段和数量,确认前零删除调用;删除后重新按 ID 查询验证不存在,状态未知时禁止自动重试。
|
||||
|
||||
## 命令格式
|
||||
|
||||
```
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 行记录变更历史(record history-list)
|
||||
|
||||
> 加载边界:仅在审计某条真实 recordId 的历史变更时读取。历史分页与当前记录查询是不同契约;未遍历完成不得声称“全部历史”,历史事件也不能直接当作当前 cells 状态。
|
||||
|
||||
按 recordId 查询单条记录的全部变更历史,用于审计、回溯字段变更、定位操作人。
|
||||
|
||||
## 命令
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 行命名规则枚举键(recordNameKey)映射
|
||||
|
||||
> 加载边界:仅在配置 Table 的行称谓时读取。自然语言名称必须映射为已审阅枚举键;本页不用于记录查找、字段名解析或写入 cells。
|
||||
|
||||
`dws aitable table update --record-name-key <枚举键>` 用于设置数据表的"行命名规则"——卡片/详情页里"行"的展示别名。**取值是固定枚举,不是字段 ID**;传非法值服务端返回 `INVALID_RECORD_NAME_KEY`。
|
||||
|
||||
## 中文 → 枚举键(按 UI 下拉顺序)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# record query — 查询记录
|
||||
|
||||
> 加载边界:用于目标 Base/Table 已确定后的记录查询、筛选、排序和分页。按 ID、关键词和 filters 三种模式先分流;全量结论必须检查 `--all`、page-limit、hasMore/nextCursor 和 stop reason,默认一页只能代表当前页。
|
||||
|
||||
## 命令格式
|
||||
|
||||
```
|
||||
@@ -60,7 +62,7 @@ dws aitable record query --base-id X --table-id Y --all --cursor "上次返回
|
||||
{"operator":"and","operands":[{"operator":"eq","operands":["<fieldId>","<value>"]}]}
|
||||
```
|
||||
|
||||
> **singleSelect/multipleSelect 过滤**:filters 中可传 option id 或 option name,但建议优先用 **option id**(通过 `field get` 获取),更可靠。
|
||||
> **singleSelect/multipleSelect 过滤**:filters 中传选项名称字面量,不传 option id。选项名称先通过 `field get` 的 `config.options[]` 核对。
|
||||
|
||||
## 减少响应体积
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 行记录分享链接(record share-url)
|
||||
|
||||
> 加载边界:仅在用户需要某条真实记录的分享链接时读取。recordId 必须属于当前 Base/Table;可选 viewId 也必须同表。生成链接不等于修改记录权限或把链接发送给他人。
|
||||
|
||||
按 recordId 批量获取记录的分享链接,把某行单独发给同事查看。
|
||||
|
||||
## 命令
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# record update — 更新记录
|
||||
|
||||
> 加载边界:仅在 recordId 和待修改 fieldId 已确定后读取。只提交用户要求变更的 cells;写前读取字段类型/只读性,写后使用返回 recordId 回读。未返回 ID、字段未变化或状态未知都不能声称成功。
|
||||
|
||||
## 命令格式
|
||||
|
||||
```
|
||||
@@ -17,23 +19,17 @@ Flags:
|
||||
|
||||
只需传入需修改的字段,未传入的保持原值。每条记录必须含 recordId 和 cells。
|
||||
|
||||
## cells key:优先使用 fieldId,也支持唯一字段名
|
||||
## cells key:自动化与 Agent 路径使用 fieldId
|
||||
|
||||
`cells` 的 key 有两种写法:
|
||||
公开稳定的 Agent/自动化写法是 `fieldId`:它不受字段重命名或重名影响,并可通过 `field get` 获取。
|
||||
|
||||
- fieldId(推荐):不受字段重命名或重名影响,通过 `field get` 获取。
|
||||
- 当前表内唯一的字段名:按名称精确匹配;如果存在同名字段,必须改用 fieldId。
|
||||
|
||||
同一字段同时通过 fieldId 和字段名传入时,fieldId 对应的值优先。
|
||||
Runtime 仍兼容当前表内唯一字段名,但这属于便捷兼容路径:重名或重命名会改变解析结果,不能用于脚本、跨步骤调用或从旧上下文重放。字段名与 fieldId 同时出现时也不要依赖覆盖顺序。
|
||||
|
||||
```bash
|
||||
# 推荐:fieldId
|
||||
dws aitable record update --base-id <BASE_ID> --table-id <TABLE_ID> \
|
||||
--records '[{"recordId":"recXXX","cells":{"fldStatusId":"已完成"}}]' --format json
|
||||
|
||||
# 便捷写法:当前表内唯一字段名
|
||||
dws aitable record update --base-id <BASE_ID> --table-id <TABLE_ID> \
|
||||
--records '[{"recordId":"recXXX","cells":{"状态":"已完成"}}]' --format json
|
||||
```
|
||||
|
||||
## 推荐参数形式
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 行记录 Upsert(record upsert)
|
||||
|
||||
> 加载边界:仅在同一批次明确混合“带 recordId 更新”和“不带 recordId 创建”时读取。若目标匹配逻辑仍依赖名称或业务键,先 query 并消歧;upsert 不负责猜测 recordId,部分成功必须返回逐项 ledger。
|
||||
|
||||
按 `recordId` 是否存在,自动把入参拆分到 update 链路或 create 链路:批次混合"已存在改 + 新出现建"时用,省掉客户端按 ID 分批的逻辑。
|
||||
|
||||
## 命令
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 视图配置(view get/update <attr>)
|
||||
|
||||
> 加载边界:仅在创建视图或读取/修改视图配置时加载。先确认 viewType 与属性支持矩阵,并绑定当前 Base/Table/viewId;写后读取同一属性验证。复制、锁定、行高和高亮规则改读 [aitable-view-extras.md](aitable-view-extras.md)。
|
||||
|
||||
按属性局部读/写视图配置。每个属性独立子命令,typed flag 友好,agent 不必拼 JSON。
|
||||
向后兼容:`view update --config '{...}'` 一次多属性入口仍可用。
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 视图扩展操作(lock / frozen-cols / row-height / fill-color-rule / duplicate)
|
||||
|
||||
> 加载边界:仅在用户明确操作锁定、冻结列、行高、高亮规则或复制视图时读取。所有动作复用当前 Base/Table/viewId;viewType 不支持时本地停止,写后用对应 get 或新 viewId 回读。
|
||||
|
||||
本文档讲 5 项视图操作命令:
|
||||
|
||||
- 锁定 / 解锁视图:`view lock` / `view get lock`
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# workflow — 自动化工作流管理
|
||||
|
||||
> 加载边界:仅在创建、全量更新、启停或检查 AITable 自动化工作流时读取。create/update 使用完整 DSL,先校验并保留现状;disable 等需确认动作确认前零写入,返回 flowId 后用 get/list 验证 `valid/issues/status`。
|
||||
|
||||
创建 / 更新 / 启停 / 查看 / 列出 Base 下的自动化工作流("当 X 时自动 Y" 流程)。
|
||||
适用场景:用户要求创建自动化、修改流程、停掉流程、查询已有流程或恢复运行。
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# 易混淆操作与字段规则
|
||||
|
||||
> 定位:字段操作的轻量防错入口,仅在建表/建字段前快速核对主字段、只读字段和附件边界。完整 config 读取 `aitable/aitable-field-properties.md`,cellValue 读取 `aitable/aitable-cell-value.md`;不要与两份专题同时全量加载。
|
||||
|
||||
## 易混淆操作 (高风险场景必读)
|
||||
|
||||
| 用户说的 | 正确命令 | 不是这个 |
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# aitable 局部意图消歧
|
||||
|
||||
> 定位:只处理 AITable 与 Sheet、Doc、Drive、Minutes 等 sibling 产品的边界;产品内 Base/Table/Field/Record 路由由根 Skill 负责。用户意图已经明确属于 AITable 时无需加载本页。
|
||||
|
||||
本文件从单 Skill `intent-guide.md` 拆分而来,仅保留与本产品相关的跨产品消歧规则。
|
||||
|
||||
| 用户说... | 真实意图 | 应该用 | 不要用 | 理由 |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user