Responsibility: Turn the user's intent → build a visually rich v2 card JSON, write it to
/tmp/<name>-card.json, then hand it tofeishu-cli msg send --msg-type interactive --content-fileto send.Only do v2: All output must start with
"schema": "2.0", usingbodyto wrap elements. For v1-related migration, seereferences/v2-vs-v1.md。
1. Decision tree (choose a template in 30 seconds)
用户意图 → 推荐模板 → header.template
├─ 通知 / 公告 / 信息同步 → notification.json → blue
├─ 操作完成 / 构建成功 / 交付 → success-report.json → green
├─ 告警 / 故障 / 错误 → alert.json → red / carmine
├─ 审批请求 / 待办 / 确认 → approval.json → orange
├─ 数据展示 / 报表 / 指标 → data-dashboard.json → purple / indigo
├─ 长文摘要 / 文章总结 / 多章节 → article-summary.json → blue / wathet
└─ AI 流式输出 / 生成中 → llm-streaming.json → blue + streaming_mode
When unsure, use notification.json as a fallback(blue, generic structure, broadest coverage).
The user didn't specify the exact scenario but the content has structure (multiple fields/links/charts/status metrics)—also prefer a card over plain text.
2. v2 minimal runnable skeleton
{
"schema": "2.0",
"config": {
"update_multi": true,
"enable_forward": true,
"width_mode": "fill"
},
"header": {
"template": "blue",
"title": { "tag": "plain_text", "content": "标题" },
"subtitle": { "tag": "plain_text", "content": "可选副标题" }
},
"body": {
"direction": "vertical",
"vertical_spacing": "medium",
"elements": [
{ "tag": "markdown", "content": "**正文**\n\n支持完整 CommonMark + `<font>`/`<at>`/`<link>` 等 HTML 标签" }
]
}
}
Four essential blocks:
schema: "2.0"— top-level declaration, required in v2config— global behavior:update_multiv2 only supportstrue;width_modeusefill/compact/omitted (default 600px)header— title area:templatesets the theme color,titleis required,subtitleis optionalbody.elements— component array, rendered in order from top to bottom
3. Workflow (6 steps)
- Choose template: Determine
templates/<name>.jsonaccording to the decision tree, and Read it in as the starting point - Fill in content: Replace all
__PLACEHOLDER__placeholders (title, field values, URL, data, personnel ID) - Choose color scheme:
header.templatePick according to the scenario (seereferences/design.mdcolor matrix), markdown<font>no more than 3 accent colors - Validate:
python -m json.tool /tmp/xxx-card.jsonpasses JSON parsing- Check for v2-forbidden fields (see Section 6 prohibited areas)
- Save to disk: Write to
/tmp/<name>-card.json(reuse existing paths to avoid pollution) - Send:
Returns on successfeishu-cli msg send \ --receive-id-type email \ --receive-id user@example.com \ --msg-type interactive \ --content-file /tmp/<name>-card.jsonmessage_id: om_xxx。
4. Component quick reference matrix
Display type(view only, no interaction):
| tag | Purpose | Typical properties |
|---|---|---|
markdown |
Rich text body | content + text_size + text_align + icon |
div |
Multi-column key-value with fields | fields: [{is_short, text}] |
hr |
Divider | No other properties |
img |
Single image | img_key + alt |
img_combination |
Image combination (grid/carousel) | combination_layout + img_list |
chart |
VChart chart | chart_spec + aspect_ratio + color_theme |
person |
Single person | user_id + show_name + show_avatar |
person_list |
Person list | persons: [{user_id}] |
table |
Table | rows + columns + row_height |
Container type(holds other components):
| tag | Purpose | Key properties |
|---|---|---|
column_set |
Horizontal columns | flex_mode + horizontal_spacing + columns |
collapsible_panel |
Collapsible panel | expanded + header.{title,icon,background_color} + elements |
form |
Form container | name + elements(collect a batch of inputs then submit all at once) |
interactive_container |
Interactive container | Unified styling and click interaction, wraps multiple components |
Interactive type(clickable/inputtable):
| tag | Purpose | Key properties |
|---|---|---|
button |
Button | type + text + `behaviors: [{type: "open_url" |
input |
Single-line input | name + placeholder + default_value |
textarea |
Multi-line input | Same as input, multiple rows |
select_static |
Dropdown single-select | options: [{text, value}] |
multi_select_static |
Dropdown multi-select | Same as above,selected_values array |
date_picker |
Date | initial_date + placeholder |
picker_time |
Time | Same as above |
picker_datetime |
Date-time | Same as above |
overflow |
Collapsible menu (⋯ button) | options: [{text, value, url}] |
Complete fields, nested rules, pitfalls → read references/components.md。
5. The three axes of beauty (overview)
5.1 Color
header.templateChoose one primary color from the 13 options (seedesign.mdcolor matrix):- blue = notification / information | green = success | orange = warning | red = error | grey = archive | purple = brand/data
- For inline color in markdown, only use
<font color='...'>to highlight key numbers,a card should use no more than 3 primary colors - Use
greyto reduce the visual weight of secondary text
5.2 Layout rhythm
body.vertical_spacingSetmedium(8px) as the default rhythm- Important information →
large(12px) for more separation - Compact rows (such as status pills) →
small(4px) - Scenario switch → insert
hras a hard divider
5.3 Element density (Z-shaped visual path)
顶部 │ header(标题 + 副标题 + 最多 3 个 text_tag)
中上 │ markdown 摘要(核心结论,带 1-2 处 <font> 强调)
中部 │ div.fields(2×2 关键指标) 或 column_set(左图右文)
中下 │ chart / table(数据可视化)
底部 │ collapsible_panel(折叠次要信息)
操作区│ column_set 里放 2-3 个 button(primary 放中间)
See details in references/design.md。
6. Forbidden zones (v2 strict validation, unknown attributes will error!)
| ❌ Forbidden | ✅ Replacement | Reason |
|---|---|---|
Top-level elements placed directly |
Put into body.elements |
v2 must be wrapped in body |
"tag": "action" interactive module |
Place buttons directly in body.elements, use spacing for spacing |
v2 deprecates the action module |
"tag": "note" note component |
markdown + <font color='grey'> + text_size: "notation" |
v2 deprecates note |
config.wide_screen_mode |
config.width_mode: "fill" |
v1 legacy attribute |
config.update_multi: false |
update_multi: true(or omit) |
v2 only supports true |
i18n_elements |
i18n_content(local multilingual) |
v2 does not support global multilingual |
hex color #FF0000 |
color enum red or rgba(255,0,0,1) |
v2 does not recognize hex |
[text]($urlVal) differentiated jump |
<link icon='' url='' pc_url='' ...> tag |
removed in v2 |
stretch_without_padding full-width image |
margin: "0 -12px" negative margin |
removed in v2 |
flex_mode: "fixed" |
flex_mode: "none" or "bisect"/"trisect" |
enum does not include fixed |
column.width: 1/2/3 number |
width: "weighted" + weight: 1~5 |
column width syntax changed |
collapsible_panel.header.padding: "4px 8px" two values |
Write as "4px 8px 4px 8px" four values |
The server strictly validates header padding and only accepts a single value or four values |
Fallback check: after writing the JSON, search for note|action|wide_screen_mode, and change any that appear.
7. Complete example (ready to copy)
{
"schema": "2.0",
"config": { "update_multi": true, "enable_forward": true, "width_mode": "fill" },
"header": {
"template": "blue",
"title": { "tag": "plain_text", "content": "🚀 本周迭代完成" },
"subtitle": { "tag": "plain_text", "content": "2026-04-17 发版报告" }
},
"body": {
"direction": "vertical",
"vertical_spacing": "medium",
"elements": [
{ "tag": "markdown", "content": "共交付 **12 个功能** / **修复 8 个 Bug**,<font color='green'>全部测试通过</font>。" },
{ "tag": "hr" },
{
"tag": "div",
"fields": [
{ "is_short": true, "text": { "tag": "lark_md", "content": "**📦 版本**\n`v2.3.0`" } },
{ "is_short": true, "text": { "tag": "lark_md", "content": "**⏱ 耗时**\n5 天" } },
{ "is_short": true, "text": { "tag": "lark_md", "content": "**👥 参与**\n7 人" } },
{ "is_short": true, "text": { "tag": "lark_md", "content": "**✅ 状态**\n<font color='green'>已上线</font>" } }
]
},
{
"tag": "collapsible_panel",
"expanded": false,
"header": {
"title": { "tag": "markdown", "content": "**📋 详细变更清单(点击展开)**" },
"background_color": "grey",
"padding": "4px 8px 4px 8px",
"icon": { "tag": "standard_icon", "token": "down-small-ccm_outlined", "size": "16px 16px" },
"icon_position": "right",
"icon_expanded_angle": -180
},
"elements": [
{ "tag": "markdown", "content": "- 新增卡片 V2 构造能力\n- 支持流式更新\n- 修复图表渲染问题" }
]
},
{
"tag": "column_set",
"flex_mode": "none",
"horizontal_spacing": "8px",
"columns": [
{
"tag": "column", "width": "weighted", "weight": 1, "vertical_align": "center",
"elements": [{
"tag": "button",
"text": { "tag": "plain_text", "content": "📖 发版文档" },
"type": "primary", "width": "fill",
"behaviors": [{ "type": "open_url", "default_url": "https://example.com/release" }]
}]
},
{
"tag": "column", "width": "weighted", "weight": 1, "vertical_align": "center",
"elements": [{
"tag": "button",
"text": { "tag": "plain_text", "content": "📊 监控大盘" },
"type": "default", "width": "fill",
"behaviors": [{ "type": "open_url", "default_url": "https://example.com/monitor" }]
}]
}
]
},
{ "tag": "markdown", "content": "<font color='grey'>📡 由 feishu-cli-card 技能生成 · v2 schema 2.0</font>" }
]
}
}
8. Send handoff
# 方式 1:直接发给默认接收人(env FEISHU_OWNER_EMAIL 或 user@example.com)
feishu-cli msg send \
--receive-id-type email \
--receive-id user@example.com \
--msg-type interactive \
--content-file /tmp/my-card.json
# 方式 2:发群
feishu-cli msg send --receive-id-type chat_id --receive-id oc_xxx \
--msg-type interactive --content-file /tmp/my-card.json
# 方式 3:带本地图片(--upload-images 自动上传 markdown 里的 ./img.png)
feishu-cli msg send --receive-id-type email --receive-id ... \
--msg-type interactive --content-file /tmp/my-card.json --upload-images
Success indicator: returns 消息 ID: om_xxxxx. If it reports content format error or invalid element,
go back to the forbidden zone table in section 6 and check item by item.
Deep resources
Read as needed:
| File | When to read | Scale |
|---|---|---|
references/components.md |
Need to use a component but can't remember the fields / need to look up nesting rules / need to look up color enums | ~400 lines, with TOC |
references/design.md |
Need to determine color scheme / layout rhythm / scenario → template mapping | ~250 lines |
references/v2-vs-v1.md |
Have v1 legacy code to migrate / need to explain why the user's v1 example doesn't work | ~120 lines |
references/vchart-quickref.md |
Need to draw charts (bar/pie/line/radar/gauge, etc.) | ~180 lines |
templates/<name>.json |
7 out-of-the-box skeletons; always pick one first instead of writing from scratch | each 80-150 lines |
Boundary with feishu-cli-msg
- feishu-cli-card (this skill):Construct v2 card JSON. The responsibility is "write it beautifully."
- feishu-cli-msg:Send messages (various types, including interactive). The responsibility is "get it delivered."
Construction complete → call msg to send. The two are decoupled; do not write them mixed together.