Compare commits

...

4 Commits

Author SHA1 Message Date
huangmengxuan
0ff9c84cd8 feat(skill): add relative-path-only rule to lark-shared security section
lark-cli rejects absolute paths for --file, --output, --output-dir,
and @file with 'unsafe file path'. Document this in lark-shared so
agents know to use cwd-relative paths or stdin for data input.

Change-Id: I50cf801c2c5d0e3cbb98a76e1752d410518c8636
2026-06-03 13:59:06 +08:00
huangmengxuan
175c9f6ffc feat(skill): add CRITICAL instruction to lark-task, lark-contact, lark-slides
Same enforcement as the previous commit — require reading reference
docs (or -h) before calling shortcuts. These three skills use
non-standard section headers but still have shortcut tables with
reference links.

Change-Id: I5170cc763c15e3030c4117a36af36c9f1e94501e
2026-06-03 13:59:06 +08:00
huangmengxuan
575bcc407b feat(skill): add CRITICAL instruction to enforce reference reading before shortcut execution
Evaluation data shows AI call failure rate <1% when reference docs are
read vs ~29% when not. Add a CRITICAL line to the Shortcuts section of
14 SKILL.md files and the skill template, requiring agents to read the
linked reference doc (or run -h for commands without one) before
invoking any shortcut.

Change-Id: Ia4204518eb43a9f6c8295b95633ee5d9cf2f5352
2026-06-03 13:59:06 +08:00
MaxHuang22
24ce3ec151 feat: add --json flag as no-op alias for --format json (#1104)
* feat(api): add --json flag as no-op alias for --format json

* feat(service): add --json flag as no-op alias for --format json

* feat(shortcut): add --json flag as no-op alias for --format json

Skip registration when a custom --json flag already exists on the
command (e.g. base shortcuts use --json for body input).

Change-Id: If66236cadeea7fa81811061cce775deff51b92ce
2026-06-03 13:58:14 +08:00
25 changed files with 156 additions and 2 deletions

View File

@@ -90,6 +90,7 @@ func NewCmdApiWithContext(ctx context.Context, f *cmdutil.Factory, runF func(*AP
cmd.Flags().IntVar(&opts.PageLimit, "page-limit", 10, "max pages to fetch with --page-all (0 = unlimited)")
cmd.Flags().IntVar(&opts.PageDelay, "page-delay", 200, "delay in ms between pages")
cmd.Flags().StringVar(&opts.Format, "format", "json", "output format: json|ndjson|table|csv")
cmd.Flags().Bool("json", false, "shorthand for --format json")
cmd.Flags().StringVarP(&opts.JqExpr, "jq", "q", "", "jq expression to filter JSON output")
cmd.Flags().BoolVar(&opts.DryRun, "dry-run", false, "print request without executing")
cmd.Flags().StringVar(&opts.File, "file", "", "file to upload as multipart/form-data ([field=]path, supports - for stdin)")

View File

@@ -718,3 +718,23 @@ func TestApiCmd_PermissionError_DerivesFirstClassFields(t *testing.T) {
t.Errorf("LogID = %q, want %q", pe.LogID, "20260527-test-log")
}
}
func TestApiCmd_JsonFlag_Accepted(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, &core.CliConfig{
AppID: "test-app", AppSecret: "test-secret", Brand: core.BrandFeishu,
})
var gotOpts *APIOptions
cmd := NewCmdApi(f, func(opts *APIOptions) error {
gotOpts = opts
return nil
})
cmd.SetArgs([]string{"GET", "/open-apis/test", "--json"})
err := cmd.Execute()
if err != nil {
t.Fatalf("--json should be accepted without error, got: %v", err)
}
if gotOpts.Method != "GET" {
t.Errorf("expected method GET, got %s", gotOpts.Method)
}
}

View File

@@ -180,6 +180,7 @@ func NewCmdServiceMethodWithContext(ctx context.Context, f *cmdutil.Factory, spe
cmd.Flags().IntVar(&opts.PageLimit, "page-limit", 10, "max pages to fetch with --page-all (0 = unlimited)")
cmd.Flags().IntVar(&opts.PageDelay, "page-delay", 200, "delay in ms between pages")
cmd.Flags().StringVar(&opts.Format, "format", "json", "output format: json|ndjson|table|csv")
cmd.Flags().Bool("json", false, "shorthand for --format json")
cmd.Flags().StringVarP(&opts.JqExpr, "jq", "q", "", "jq expression to filter JSON output")
cmd.Flags().BoolVar(&opts.DryRun, "dry-run", false, "print request without executing")
if risk == "high-risk-write" {

View File

@@ -765,3 +765,22 @@ func TestDetectFileFields(t *testing.T) {
})
}
}
func TestServiceMethod_JsonFlag_Accepted(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, testConfig)
var captured *ServiceMethodOptions
cmd := NewCmdServiceMethod(f, driveSpec(),
map[string]interface{}{"description": "desc", "httpMethod": "GET"}, "list", "files",
func(opts *ServiceMethodOptions) error {
captured = opts
return nil
})
cmd.SetArgs([]string{"--json"})
if err := cmd.Execute(); err != nil {
t.Fatalf("--json should be accepted without error, got: %v", err)
}
if captured == nil {
t.Fatal("expected runF to be called")
}
}

View File

@@ -1176,6 +1176,9 @@ func registerShortcutFlagsWithContext(ctx context.Context, cmd *cobra.Command, f
cmdutil.RegisterFlagCompletion(cmd, "format", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"json", "pretty", "table", "ndjson", "csv"}, cobra.ShellCompDirectiveNoFileComp
})
if cmd.Flags().Lookup("json") == nil {
cmd.Flags().Bool("json", false, "shorthand for --format json")
}
}
if s.Risk == "high-risk-write" {
cmd.Flags().Bool("yes", false, "confirm high-risk operation")

View File

@@ -96,3 +96,76 @@ func TestShortcutMount_FlagCompletionsDisabled(t *testing.T) {
t.Fatal("did not expect completion func for --format when disabled")
}
}
func TestShortcutMount_JsonFlag_AcceptedWhenHasFormat(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, nil)
parent := &cobra.Command{Use: "root"}
shortcut := Shortcut{
Service: "test",
Command: "+read",
Description: "test read",
HasFormat: true,
Execute: func(context.Context, *RuntimeContext) error { return nil },
}
shortcut.Mount(parent, f)
cmd, _, err := parent.Find([]string{"+read"})
if err != nil {
t.Fatalf("Find() error = %v", err)
}
if flag := cmd.Flags().Lookup("json"); flag == nil {
t.Fatal("expected --json flag to be registered on HasFormat shortcut")
}
}
func TestShortcutMount_JsonFlag_SkippedWhenConflict(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, nil)
parent := &cobra.Command{Use: "root"}
shortcut := Shortcut{
Service: "test",
Command: "+update",
Description: "test update",
HasFormat: true,
Flags: []Flag{
{Name: "json", Desc: "body JSON object", Required: true},
},
Execute: func(context.Context, *RuntimeContext) error { return nil },
}
shortcut.Mount(parent, f)
cmd, _, err := parent.Find([]string{"+update"})
if err != nil {
t.Fatalf("Find() error = %v", err)
}
// --json flag exists (from custom Flags), but should be the string type, not bool.
flag := cmd.Flags().Lookup("json")
if flag == nil {
t.Fatal("expected --json flag from custom Flags")
}
if flag.DefValue != "" {
t.Errorf("expected empty default (string flag), got %q", flag.DefValue)
}
}
func TestShortcutMount_JsonFlag_RegisteredWithoutHasFormat(t *testing.T) {
f, _, _, _ := cmdutil.TestFactory(t, nil)
parent := &cobra.Command{Use: "root"}
shortcut := Shortcut{
Service: "test",
Command: "+write",
Description: "test write",
HasFormat: false,
Execute: func(context.Context, *RuntimeContext) error { return nil },
}
shortcut.Mount(parent, f)
cmd, _, err := parent.Find([]string{"+write"})
if err != nil {
t.Fatalf("Find() error = %v", err)
}
// --format is now registered for all shortcuts (regardless of HasFormat),
// so --json should also be present.
if flag := cmd.Flags().Lookup("json"); flag == nil {
t.Fatal("expected --json flag to be registered even when HasFormat is false")
}
}

View File

@@ -18,8 +18,10 @@ metadata:
Shortcut 是对常用操作的高级封装(`lark-cli {{service}} +<verb> [flags]`)。有 Shortcut 的操作优先使用。
| Shortcut | 说明 |
|----------|------|
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 | Reference |
|----------|------|-----------|
{{shortcut_rows}}
{{/shortcuts}}
{{#actions}}

View File

@@ -96,6 +96,8 @@ lark-cli auth login --domain apps
Shortcut 是对常用操作的高级封装(`lark-cli apps +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+create`](references/lark-apps-create.md) | 创建妙搭应用name / description / icon-url |

View File

@@ -70,6 +70,8 @@ Calendar (日历)
Shortcut 是对常用操作的高级封装(`lark-cli calendar +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+agenda`](references/lark-calendar-agenda.md) | 查看日程安排(默认今天) |

View File

@@ -14,6 +14,8 @@ metadata:
**user 身份和 bot 身份是两条完全独立的路径**。先确定当前身份,再按下表选命令:
**CRITICAL — 执行任何命令之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| 想做什么 | user 身份 | bot 身份 |
|---|---|---|
| 按姓名 / 邮箱搜员工拿 open_id | [`+search-user`](references/lark-contact-search-user.md) | 不支持 |

View File

@@ -59,6 +59,8 @@ lark-cli docs +update --api-version v2 --doc "文档URL或token" --command appen
Shortcut 是对常用操作的高级封装(`lark-cli docs +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+create`](references/lark-doc-create.md) | Create a Lark document (XML / Markdown) |

View File

@@ -260,6 +260,8 @@ lark-cli drive permission.members create \
Shortcut 是对常用操作的高级封装(`lark-cli drive +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`+search`](references/lark-drive-search.md) | Search Lark docs, Wiki, and spreadsheet files with flat filter flags. Natural-language-friendly: `--edited-since`, `--mine`, `--doc-types`, etc. |

View File

@@ -70,6 +70,8 @@ Item types for feed-layer flags:
Shortcut 是对常用操作的高级封装(`lark-cli im +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+chat-create`](references/lark-im-chat-create.md) | Create a group chat or topic chat; user/bot; --chat-mode group|topic; private/public; invites users/bots; optionally sets bot manager |

View File

@@ -464,6 +464,8 @@ lark-cli mail user_mailbox.folders create \
Shortcut 是对常用操作的高级封装(`lark-cli mail +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+message`](references/lark-mail-message.md) | Use when reading full content for a single email by message ID. Returns normalized body content plus attachments metadata, including inline images. |

View File

@@ -41,6 +41,8 @@ metadata:
Shortcut 是对常用操作的高级封装(`lark-cli markdown +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+create`](references/lark-markdown-create.md) | Create a Markdown file in Drive |

View File

@@ -111,6 +111,8 @@ Minutes (妙记) ← minute_token 标识
Shortcut 是对常用操作的高级封装(`lark-cli minutes +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
| -------------------------------------------------- | --------------------------------------------------------------- |
| [`+search`](references/lark-minutes-search.md) | Search minutes by keyword, owners, participants, and time range |

View File

@@ -16,6 +16,8 @@ metadata:
Shortcut 是对常用操作的高级封装(`lark-cli okr +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|--------------------------------------------------------------|--------------------------|
| [`+cycle-list`](references/lark-okr-cycle-list.md) | 获取特定用户的 OKR 周期列表,可以按时间筛选 |

View File

@@ -123,6 +123,7 @@ lark-cli 命令执行后如果检测到新版本JSON 输出中会包含 `_
## 安全规则
- **文件路径只接受相对路径**`--file`、`--output`、`--output-dir`、`@file` 等路径参数只接受 cwd 下的相对路径,传绝对路径会报 `unsafe file path`。数据输入(`@file`、大 JSON优先用 stdin 传入,避免路径和转义问题。
- **禁止输出密钥**appSecret、accessToken到终端明文。
- **写入/删除操作前必须确认用户意图**。
- 用 `--dry-run` 预览危险请求。

View File

@@ -179,6 +179,8 @@ lark-cli sheets +write --url "URL" --sheet-id "sheetId" --range "C6" \
Shortcut 是对常用操作的高级封装(`lark-cli sheets +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
### Spreadsheet Management
对应参考文档:[spreadsheet-management](references/lark-sheets-spreadsheet-management.md)

View File

@@ -256,6 +256,8 @@ Slides (演示文稿)
Shortcut 是对常用操作的高级封装(`lark-cli slides +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+create`](references/lark-slides-create.md) | 创建 PPT可选 `--slides` 一步添加页面,支持 `<img src="@./local.png">` 占位符自动上传) |

View File

@@ -33,6 +33,8 @@ metadata:
> Task OpenAPI 中用于更新/操作任务的 `guid` 是任务的全局唯一标识GUID不是客户端展示的任务编号例如 `t104121` / `suite_entity_num`)。
> 对于 Feishu 的任务 applink例如 `.../client/todo/task?guid=...`),必须使用 URL query 里的 `guid` 参数作为 task guid。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+create`](references/lark-task-create.md) | create a task |

View File

@@ -94,6 +94,8 @@ lark-cli vc +notes --meeting-ids "$MID"
Shortcut 是对常用操作的高级封装(`lark-cli vc +<verb> [flags]`)。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 类型 | 说明 |
| --------------------------------------------------------------- | -- | -------------------------------------------------------------------------- |
| [`+meeting-join`](references/lark-vc-agent-meeting-join.md) | 写 | Join an in-progress meeting by 9-digit meeting number |

View File

@@ -137,6 +137,8 @@ Meeting (视频会议)
Shortcut 是对常用操作的高级封装(`lark-cli vc +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+search`](references/lark-vc-search.md) | Search meeting records (requires at least one filter) |

View File

@@ -33,6 +33,8 @@ metadata:
## Shortcuts
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|---|---|
| [`+query`](references/lark-whiteboard-query.md) | 查询画板,导出为预览图片、代码或原始节点结构 |

View File

@@ -59,6 +59,8 @@ metadata:
Shortcut 是对常用操作的高级封装(`lark-cli wiki +<verb> [flags]`)。有 Shortcut 的操作优先使用。
**CRITICAL — 执行任何 Shortcut 之前MUST 先用 Read 工具读取下表中对应的说明文档;没有文档链接的命令须执行 -h 了解用法。禁止盲调。**
| Shortcut | 说明 |
|----------|------|
| [`+move`](references/lark-wiki-move.md) | Move a wiki node, or move a Drive document into Wiki |