Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions shortcuts/base/base_execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4045,3 +4045,93 @@ func TestBaseViewExecutePropertyGettersAndExtendedSetters(t *testing.T) {
}
})
}

func TestBaseFieldButtonWorkflowExecute(t *testing.T) {
t.Run("bind sends wkf workflow id", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/fields/fld_x/button-workflow:bind",
BodyFilter: func(body []byte) bool {
return string(body) == `{"workflow_id":"wkf_x"}`
},
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"workflow_id": "wkf_x", "table_id": "tbl_x", "field_id": "fld_x"},
},
})
err := runShortcut(t, BaseFieldButtonBind, []string{"+field-button-bind", "--base-token", "app_x", "--table-id", "tbl_x", "--field-id", "fld_x", "--workflow-id", "wkf_x"}, factory, stdout)
if err != nil {
t.Fatalf("err=%v", err)
}
got := stdout.String()
for _, want := range []string{`"workflow_id": "wkf_x"`, `"table_id": "tbl_x"`, `"field_id": "fld_x"`} {
if !strings.Contains(got, want) {
t.Fatalf("stdout missing %q:\n%s", want, got)
}
}
})

t.Run("bind rejects internal numeric workflow id", func(t *testing.T) {
factory, stdout, _ := newExecuteFactory(t)
err := runShortcut(t, BaseFieldButtonBind, []string{"+field-button-bind", "--base-token", "app_x", "--table-id", "tbl_x", "--field-id", "fld_x", "--workflow-id", "123"}, factory, stdout)
if err == nil || !strings.Contains(err.Error(), "wkf prefix") {
t.Fatalf("err=%v", err)
}
Comment on lines +4075 to +4080

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Assert the typed validation contract.

This test checks only the "wkf prefix" message. It will accept an untyped error with the same text.

Use errs.ProblemOf to assert the validation category and subtype. Use errors.As with *errs.ValidationError to assert Param == "--workflow-id".

As per coding guidelines, error-path tests must assert typed metadata instead of relying only on message substrings. Based on learnings, use errors.As because errs.ProblemOf does not expose Param.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/base/base_execute_test.go` around lines 4075 - 4080, The “bind
rejects internal numeric workflow id” test currently validates only an
error-message substring. Update it to use errs.ProblemOf for the expected
validation category and subtype, and errors.As with *errs.ValidationError to
verify Param equals "--workflow-id", while retaining the existing rejection
assertion.

Sources: Coding guidelines, Learnings

})

t.Run("get field binding", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/fields/fld_x/button-workflow",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"workflow_id": "wkf_x"},
},
})
err := runShortcut(t, BaseFieldButtonBindingGet, []string{"+field-button-binding-get", "--base-token", "app_x", "--table-id", "tbl_x", "--field-id", "fld_x"}, factory, stdout)
if err != nil {
t.Fatalf("err=%v", err)
}
if got := stdout.String(); !strings.Contains(got, `"workflow_id": "wkf_x"`) {
t.Fatalf("stdout=%s", got)
}
})

t.Run("unbind", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "POST",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_x/fields/fld_x/button-workflow:unbind",
Body: map[string]interface{}{"code": 0, "data": map[string]interface{}{"unbound": true}},
})
err := runShortcut(t, BaseFieldButtonUnbind, []string{"+field-button-unbind", "--base-token", "app_x", "--table-id", "tbl_x", "--field-id", "fld_x"}, factory, stdout)
if err != nil {
t.Fatalf("err=%v", err)
}
if got := stdout.String(); !strings.Contains(got, `"unbound": true`) {
t.Fatalf("stdout=%s", got)
}
})

t.Run("workflow button fields", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/workflows/wkf_x/button-fields",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"items": []interface{}{map[string]interface{}{"table_id": "tbl_x", "field_id": "fld_x"}}},
},
})
err := runShortcut(t, BaseWorkflowButtonFields, []string{"+workflow-button-fields", "--base-token", "app_x", "--workflow-id", "wkf_x"}, factory, stdout)
if err != nil {
t.Fatalf("err=%v", err)
}
got := stdout.String()
if !strings.Contains(got, `"table_id": "tbl_x"`) || !strings.Contains(got, `"field_id": "fld_x"`) {
t.Fatalf("stdout=%s", got)
}
})
}
4 changes: 2 additions & 2 deletions shortcuts/base/base_shortcuts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,13 @@ func TestShortcutsCatalog(t *testing.T) {
"+url-resolve", "+title-resolve",
"+base-block-list", "+base-block-create", "+base-block-move", "+base-block-rename", "+base-block-delete",
"+table-list", "+table-get", "+table-create", "+table-update", "+table-delete", "+table-copy", "+table-copy-status",
"+field-list", "+field-get", "+field-create", "+field-update", "+field-delete", "+field-search-options",
"+field-list", "+field-get", "+field-create", "+field-update", "+field-delete", "+field-search-options", "+field-button-bind", "+field-button-binding-get", "+field-button-unbind",
"+view-list", "+view-get", "+view-create", "+view-delete", "+view-get-filter", "+view-set-filter", "+view-get-visible-fields", "+view-set-visible-fields", "+view-get-group", "+view-set-group", "+view-get-sort", "+view-set-sort", "+view-get-timebar", "+view-set-timebar", "+view-get-card", "+view-set-card", "+view-rename",
"+record-list", "+record-search", "+record-get", "+record-upsert", "+record-batch-create", "+record-batch-update", "+record-share-link-create", "+record-upload-attachment", "+record-download-attachment", "+record-remove-attachment", "+record-delete",
"+record-history-list",
"+base-get", "+base-copy", "+base-create",
"+role-create", "+role-delete", "+role-update", "+role-list", "+role-get", "+advperm-enable", "+advperm-disable",
"+workflow-list", "+workflow-get", "+workflow-create", "+workflow-update", "+workflow-enable", "+workflow-disable",
"+workflow-list", "+workflow-get", "+workflow-create", "+workflow-update", "+workflow-enable", "+workflow-disable", "+workflow-button-fields",
"+data-query",
"+form-create", "+form-delete", "+form-list", "+form-update", "+form-get", "+form-detail",
"+form-questions-create", "+form-questions-delete", "+form-questions-update", "+form-questions-list",
Expand Down
39 changes: 39 additions & 0 deletions shortcuts/base/field_button_bind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT

package base

import (
"context"

"github.com/larksuite/cli/shortcuts/common"
)

var BaseFieldButtonBind = common.Shortcut{
Service: "base",
Command: "+field-button-bind",
Description: "Bind or rebind a button field to a workflow",
Risk: "write",
Scopes: []string{"base:field:update", "base:workflow:update"},
AuthTypes: authTypes(),
Flags: []common.Flag{
baseTokenFlag(true),
tableRefFlag(true),
fieldRefFlag(true),
{Name: "workflow-id", Desc: "workflow ID returned by +workflow-create or +workflow-list (wkf... prefix)", Required: true},
},
Tips: []string{
`Example: lark-cli base +field-button-bind --base-token <base_token> --table-id <table_id> --field-id <field_id> --workflow-id <wkf_id>`,
"Create the workflow first with +workflow-create; new workflows are disabled until +workflow-enable is called.",
"Create the button field without a workflow ID in its property, then bind it with this command.",
"workflow-id must be the wkf... OpenAPI ID. Internal numeric workflow IDs are not accepted or displayed.",
"Binding is the source of truth; do not use property.trigger.config.id to create or update a binding.",
},
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
return validateWorkflowIDFlag(runtime)
},
DryRun: dryRunFieldButtonBind,
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
return executeFieldButtonBind(runtime)
},
}
29 changes: 29 additions & 0 deletions shortcuts/base/field_button_binding_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT

package base

import (
"context"

"github.com/larksuite/cli/shortcuts/common"
)

var BaseFieldButtonBindingGet = common.Shortcut{
Service: "base",
Command: "+field-button-binding-get",
Description: "Get the workflow binding for a button field",
Risk: "read",
Scopes: []string{"base:field:read", "base:workflow:read"},
AuthTypes: authTypes(),
Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true)},
Tips: []string{
`Example: lark-cli base +field-button-binding-get --base-token <base_token> --table-id <table_id> --field-id <field_id>`,
"Returns the workflow_id as a wkf... OpenAPI ID when a binding exists.",
"The binding source of truth is the button workflow relation, not field property.trigger.",
},
DryRun: dryRunFieldButtonBindingGet,
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
return executeFieldButtonBindingGet(runtime)
},
}
106 changes: 106 additions & 0 deletions shortcuts/base/field_button_ops.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT

package base

import (
"context"
"strings"

"github.com/larksuite/cli/shortcuts/common"
)

func validateWorkflowIDFlag(runtime *common.RuntimeContext) error {
workflowID := strings.TrimSpace(runtime.Str("workflow-id"))
if workflowID == "" {
return baseFlagErrorf("--workflow-id must not be blank")
}
if !strings.HasPrefix(workflowID, "wkf") {
return baseFlagErrorf("--workflow-id must be an OpenAPI workflow ID with wkf prefix; internal numeric workflow IDs are not accepted")
}
Comment on lines +13 to +20

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject surrounding whitespace before API execution.

validateWorkflowIDFlag validates the trimmed value. The dry-run and execute functions use the original value. An input such as " wkf_x " therefore passes validation but sends whitespace in the request path or body.

Reject leading or trailing whitespace so validation and execution use the same value.

Proposed fix
 func validateWorkflowIDFlag(runtime *common.RuntimeContext) error {
-	workflowID := strings.TrimSpace(runtime.Str("workflow-id"))
+	rawWorkflowID := runtime.Str("workflow-id")
+	workflowID := strings.TrimSpace(rawWorkflowID)
 	if workflowID == "" {
 		return baseFlagErrorf("--workflow-id must not be blank")
 	}
+	if workflowID != rawWorkflowID {
+		return baseFlagErrorf("--workflow-id must not contain leading or trailing whitespace")
+	}
 	if !strings.HasPrefix(workflowID, "wkf") {

As per coding guidelines, preserve input values faithfully and return a typed validation error when the requested input cannot be honored.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func validateWorkflowIDFlag(runtime *common.RuntimeContext) error {
workflowID := strings.TrimSpace(runtime.Str("workflow-id"))
if workflowID == "" {
return baseFlagErrorf("--workflow-id must not be blank")
}
if !strings.HasPrefix(workflowID, "wkf") {
return baseFlagErrorf("--workflow-id must be an OpenAPI workflow ID with wkf prefix; internal numeric workflow IDs are not accepted")
}
func validateWorkflowIDFlag(runtime *common.RuntimeContext) error {
rawWorkflowID := runtime.Str("workflow-id")
workflowID := strings.TrimSpace(rawWorkflowID)
if workflowID == "" {
return baseFlagErrorf("--workflow-id must not be blank")
}
if workflowID != rawWorkflowID {
return baseFlagErrorf("--workflow-id must not contain leading or trailing whitespace")
}
if !strings.HasPrefix(workflowID, "wkf") {
return baseFlagErrorf("--workflow-id must be an OpenAPI workflow ID with wkf prefix; internal numeric workflow IDs are not accepted")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/base/field_button_ops.go` around lines 13 - 20, Update
validateWorkflowIDFlag to reject, rather than normalize, workflow-id values with
leading or trailing whitespace before prefix validation succeeds. Return the
existing typed baseFlagErrorf validation error for such input, while preserving
the original value for dry-run and execution paths.

Source: Coding guidelines

return nil
}

func dryRunFieldButtonBind(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
return common.NewDryRunAPI().
POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/fields/:field_id/button-workflow:bind").
Body(map[string]interface{}{"workflow_id": runtime.Str("workflow-id")}).
Set("base_token", runtime.Str("base-token")).
Set("table_id", baseTableID(runtime)).
Set("field_id", runtime.Str("field-id"))
}

func dryRunFieldButtonBindingGet(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
return common.NewDryRunAPI().
GET("/open-apis/base/v3/bases/:base_token/tables/:table_id/fields/:field_id/button-workflow").
Set("base_token", runtime.Str("base-token")).
Set("table_id", baseTableID(runtime)).
Set("field_id", runtime.Str("field-id"))
}

func dryRunFieldButtonUnbind(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
return common.NewDryRunAPI().
POST("/open-apis/base/v3/bases/:base_token/tables/:table_id/fields/:field_id/button-workflow:unbind").
Set("base_token", runtime.Str("base-token")).
Set("table_id", baseTableID(runtime)).
Set("field_id", runtime.Str("field-id"))
}

func dryRunWorkflowButtonFields(_ context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
return common.NewDryRunAPI().
GET("/open-apis/base/v3/bases/:base_token/workflows/:workflow_id/button-fields").
Set("base_token", runtime.Str("base-token")).
Set("workflow_id", runtime.Str("workflow-id"))
}

func executeFieldButtonBind(runtime *common.RuntimeContext) error {
data, err := baseV3Call(runtime, "POST",
baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "fields", runtime.Str("field-id"), "button-workflow:bind"),
nil,
map[string]interface{}{"workflow_id": runtime.Str("workflow-id")},
)
if err != nil {
return err
}
runtime.Out(data, nil)
return nil
}

func executeFieldButtonBindingGet(runtime *common.RuntimeContext) error {
data, err := baseV3Call(runtime, "GET",
baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "fields", runtime.Str("field-id"), "button-workflow"),
nil,
nil,
)
if err != nil {
return err
}
runtime.Out(data, nil)
return nil
}

func executeFieldButtonUnbind(runtime *common.RuntimeContext) error {
data, err := baseV3Call(runtime, "POST",
baseV3Path("bases", runtime.Str("base-token"), "tables", baseTableID(runtime), "fields", runtime.Str("field-id"), "button-workflow:unbind"),
nil,
nil,
)
if err != nil {
return err
}
runtime.Out(data, nil)
return nil
}

func executeWorkflowButtonFields(runtime *common.RuntimeContext) error {
data, err := baseV3Call(runtime, "GET",
baseV3Path("bases", runtime.Str("base-token"), "workflows", runtime.Str("workflow-id"), "button-fields"),
nil,
nil,
)
if err != nil {
return err
}
runtime.Out(data, nil)
return nil
}
29 changes: 29 additions & 0 deletions shortcuts/base/field_button_unbind.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT

package base

import (
"context"

"github.com/larksuite/cli/shortcuts/common"
)

var BaseFieldButtonUnbind = common.Shortcut{
Service: "base",
Command: "+field-button-unbind",
Description: "Unbind a button field from its workflow",
Risk: "write",
Scopes: []string{"base:field:update", "base:workflow:update"},
AuthTypes: authTypes(),
Flags: []common.Flag{baseTokenFlag(true), tableRefFlag(true), fieldRefFlag(true)},
Tips: []string{
`Example: lark-cli base +field-button-unbind --base-token <base_token> --table-id <table_id> --field-id <field_id>`,
"Unbind is idempotent; repeating it should still return success when the server has no binding to delete.",
"Unbinding does not delete the field and does not disable or delete the workflow.",
},
DryRun: dryRunFieldButtonUnbind,
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
return executeFieldButtonUnbind(runtime)
},
}
4 changes: 4 additions & 0 deletions shortcuts/base/shortcuts.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ func Shortcuts() []common.Shortcut {
BaseFieldUpdate,
BaseFieldDelete,
BaseFieldSearchOptions,
BaseFieldButtonBind,
BaseFieldButtonBindingGet,
BaseFieldButtonUnbind,
BaseViewList,
BaseViewGet,
BaseViewCreate,
Expand Down Expand Up @@ -73,6 +76,7 @@ func Shortcuts() []common.Shortcut {
BaseWorkflowUpdate,
BaseWorkflowEnable,
BaseWorkflowDisable,
BaseWorkflowButtonFields,
BaseDataQuery,
BaseFormCreate,
BaseFormDelete,
Expand Down
35 changes: 35 additions & 0 deletions shortcuts/base/workflow_button_fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT

package base

import (
"context"

"github.com/larksuite/cli/shortcuts/common"
)

var BaseWorkflowButtonFields = common.Shortcut{
Service: "base",
Command: "+workflow-button-fields",
Description: "List button fields bound to a workflow",
Risk: "read",
Scopes: []string{"base:field:read", "base:workflow:read"},
AuthTypes: authTypes(),
Flags: []common.Flag{
{Name: "base-token", Desc: "base token", Required: true},
{Name: "workflow-id", Desc: "workflow ID (wkf... prefix)", Required: true},
},
Tips: []string{
`Example: lark-cli base +workflow-button-fields --base-token <base_token> --workflow-id <wkf_id>`,
"workflow-id must be a wkf... OpenAPI ID; internal numeric workflow IDs are not accepted or displayed.",
"Returns table_id and field_id pairs bound through the button workflow relation.",
},
Validate: func(ctx context.Context, runtime *common.RuntimeContext) error {
return validateWorkflowIDFlag(runtime)
},
DryRun: dryRunWorkflowButtonFields,
Execute: func(ctx context.Context, runtime *common.RuntimeContext) error {
return executeWorkflowButtonFields(runtime)
},
}
2 changes: 2 additions & 0 deletions skills/lark-base/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ metadata:
| 一次性聚合统计 | `+data-query` | 必读 [lark-base-data-analysis-sop.md](references/lark-base-data-analysis-sop.md) 和入口 [lark-base-data-query-guide.md](references/lark-base-data-query-guide.md);完整 DSL 再读 [lark-base-data-query.md](references/lark-base-data-query.md) |
| 公式字段 | `+field-create/update --json '{"type":"formula",...}'` | 必读 [formula-field-guide.md](references/formula-field-guide.md),读后再加隐藏确认 flag `--i-have-read-guide` |
| Lookup 字段 | `+field-create/update --json '{"type":"lookup",...}'` | 必读 [lookup-field-guide.md](references/lookup-field-guide.md),读后再加隐藏确认 flag `--i-have-read-guide` |
| 按钮字段 | `+workflow-create` → `+field-create --json '{"type":"button",...}'` → `+field-button-bind` | 先创建 disabled 的 ButtonTrigger Workflow,再创建不含 Workflow ID 的按钮字段,然后用 `+field-button-bind` 绑定;用 `+field-button-binding-get` / `+workflow-button-fields` 查询确认,最后按需 `+workflow-enable` |
| 表单提交 | `+form-submit` | 先读 [lark-base-form-detail.md](references/lark-base-form-detail.md) 获取题目、filter 和附件所需 `base_token`;提交 JSON 读 [lark-base-form-submit.md](references/lark-base-form-submit.md) |
| 表单题目创建/更新 | `+form-questions-create` / `+form-questions-update` | Base 内表单按 table 管理;先确定并复用真实 `table_id`。读 [lark-base-form-questions-create.md](references/lark-base-form-questions-create.md) / [lark-base-form-questions-update.md](references/lark-base-form-questions-update.md);题目显隐条件 `visible_rule` 结构见公共协议 [lark-base-filter-condition.md](references/lark-base-filter-condition.md) |
| Base 内表单管理 | `+form-list/get/create/update/delete` / `+form-questions-list/delete` | 缺少或不确定归属时,先用 `+table-list` 或 `+base-block-list` 取得真实 `table_id`;这些命令使用 `--base-token + --table-id` 并在整个工作流中复用同一 `table_id`,删除前确认目标表单 |
| 分享表单详情 | `+form-detail --share-token <share_token>` | 只接受表单分享链接里的 `share_token`,不要传 `--base-token` / `--form-id`;提交前读 [lark-base-form-detail.md](references/lark-base-form-detail.md) |
| 仪表盘与组件 | `+dashboard-*` / `+dashboard-block-*` | 提到图表/看板/block 时先读 [lark-base-dashboard.md](references/lark-base-dashboard.md);组件 `data_config` 读 [dashboard-block-data-config.md](references/dashboard-block-data-config.md);读取一个或多个图表计算结果用 `+dashboard-block-get-data`;读取完整仪表盘时按 block 类型分流,文本和不支持直接取数的图表按 reference 恢复 |
| Workflow | `+workflow-*` | 创建/更新或理解 steps 时读入口 [lark-base-workflow-guide.md](references/lark-base-workflow-guide.md) 和 steps JSON SSOT [lark-base-workflow-schema.md](references/lark-base-workflow-schema.md);list/get/enable/disable 只处理 workflow ID 与启停状态 |
| 按钮字段绑定 | `+field-button-bind` / `+field-button-binding-get` / `+field-button-unbind` / `+workflow-button-fields` | 只接受和展示 `wkf...` workflow ID;绑定事实来自独立按钮绑定接口,不来自字段 `property.trigger` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clarify which identifiers use the wkf... format.

This text can be read as saying that every shortcut accepts and displays only workflow IDs. The documented bind command also requires --field-id, and +workflow-button-fields returns button-field information. State that only workflow ID values must use the wkf... format. Keep field IDs and workflow IDs distinct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-base/SKILL.md` at line 76, Clarify the “按钮字段绑定” documentation
entry so the wkf... format applies only to workflow ID values. Explicitly
distinguish workflow IDs from the required field ID argument for the bind
command, and note that +workflow-button-fields returns button-field information
rather than implying all values are workflow IDs.

| 高级权限与角色 | `+advperm-*` / `+role-*` | 角色操作先读入口 [lark-base-role-guide.md](references/lark-base-role-guide.md);角色 create/update 或解读完整配置再读权限 JSON SSOT [role-config.md](references/role-config.md);系统角色不可删除;关闭高级权限会影响自定义角色 |

## Base 心智模型
Expand Down
Loading
Loading