- 移除 TodoItem 中的 priority、created_at 和 updated_at 字段 - 强制每个任务都必须有唯一 id,且由用户负责生成 - 修改合并模式逻辑,merge=true 下保留未提及的旧任务 - 支持已完成和已取消任务重新激活(状态改回 pending 或 in_progress) - 禁止 in_progress 状态退回到 pending,必须标记为 completed 或 cancelled - 优化状态转换校验,允许特定状态间合法切换 - 简化任务变更消息,移除详细的新增/更新/移除统计 - 更新文档和示例,明确 id 必须由用户生成和使用 - 修复和补充测试,增强状态转换和合并模式验证 - 调整任务时间戳生成逻辑,统一使用当前时间及索引 - 该变更提供更合理的任务状态机械及管理模式,提升稳定性和易用性
15 KiB
im +messages-reply
Prerequisite: Read
../lark-shared/SKILL.mdfirst to understand authentication, global parameters, and safety rules.
Reply to a specific message. Supports both user identity (--as user) and bot identity (--as bot). Also supports thread replies.
This skill maps to the shortcut: lark-cli im +messages-reply (internally calls POST /open-apis/im/v1/messages/:message_id/reply).
Safety Constraints
Replies sent by this tool are visible to other people. Before calling it, you must confirm with the user:
- Which message to reply to
- The reply content
- Which identity to use (user or bot)
Do not send a reply without explicit user approval.
When using --as bot, the reply is sent in the app's name, so make sure the app has already been added to the target chat.
When using --as user, the reply is sent as the authorized end user and requires the im:message.send_as_user and im:message scopes.
Choose The Right Content Flag
Default Selection Rule For Agents
- Prefer
--markdownfor headings, lists, links, summaries, investigation notes, or Markdown-looking content. - Use
--textfor exact plain text: logs, code, indentation-sensitive text, or literal Markdown. - Use
--contentfor exactpostJSON, titles, multiple locales, cards, or unsupported structures.
| Need | Recommended flag | Why |
|---|---|---|
| Reply with headings, lists, links, summaries, or investigation notes | --markdown |
Best default for lightweight formatting; converted to Feishu post JSON |
| Reply with plain text exactly as written | --text |
Preserves literal text; no Markdown conversion |
| Precisely control the reply payload | --content |
You provide the exact JSON |
| Reply with media | --image / --file / --video / --audio |
Shortcut uploads URLs, or cwd-relative local files automatically |
--text vs --markdown
- Use
--markdownfor lightweight formatted replies. - Use
--textfor exact plain text, especially logs, code, indentation, or literal Markdown characters. - Use
--contentwhen you need exactpostJSON, a card, a title, multiple locales, or any structure that--markdowncannot express reliably.
What --markdown Really Does
--markdown accepts Markdown-like input and converts it to the Feishu post payload required by the reply API.
The shortcut:
- Forces
msg_type=post - Resolves remote Markdown images like
 - Normalizes the Markdown for Feishu post rendering
- Wraps the final content as:
{"zh_cn":{"content":[[{"tag":"md","text":"..."}]]}}
This makes --markdown the simplest path for lightweight formatted replies.
Markdown Boundaries
- It does not promise full CommonMark / GitHub Flavored Markdown support.
- It always becomes a
postpayload with a singlezh_cnlocale. - It does not let you set a
posttitle. - Headings are rewritten:
# Titlebecomes#### Title##to######are normalized to#####when the content contains H1-H3
- Consecutive headings are separated with blank lines after heading normalization.
- Block spacing and line breaks may be normalized during conversion.
- Code blocks are preserved as code blocks.
- Excess blank lines are compressed.
- Already-uploaded
img_xxximage keys are the most reliable Markdown image input. - Local paths (e.g.
) are not supported directly in--markdownand will not be auto-uploaded. - Remote URLs (
https://...) will be auto-downloaded and uploaded at runtime; if the download or upload fails, the image is removed with a warning.
If you need a title, multiple locales, cards, unsupported rich structures, or byte-for-byte post JSON control, use --msg-type post --content ....
Image Constraint for --markdown
When using --markdown with images, prefer pre-uploading via images.create and referencing  for predictable results. Remote URLs may work but are not guaranteed.
Steps:
# 1. Upload image to get image_key
lark-cli im images create --data '{"image_type":"message"}' --file ./diagram.png
# Returns: {"image_key":"img_v3_xxxx"}
# 2. Use image_key in --markdown reply
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Result\n\n\n\nSee above for details.'
Preserving Formatting
If the reply contains multiple lines, code blocks, indentation, tabs, or a lot of escaping, prefer $'...' for either --markdown or --text.
When formatting must be preserved
Use --text plus $'...':
lark-cli im +messages-reply --message-id om_xxx --text $'Received\nI will check this today.\nOwner: alice'
lark-cli im +messages-reply --message-id om_xxx --text $'```sql\nselect * from jobs;\n```'
This keeps the reply as plain text instead of converting it to a post.
Commands
# Reply with a formatted update
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Reply\n\n- item 1\n- item 2'
# Reply with a plain one-line message
lark-cli im +messages-reply --message-id om_xxx --text "Received"
# Equivalent manual JSON
lark-cli im +messages-reply --message-id om_xxx --content '{"text":"Received"}'
# Reply as a bot
lark-cli im +messages-reply --message-id om_xxx --text "bot reply" --as bot
# Reply with preserved multi-line text
lark-cli im +messages-reply --message-id om_xxx --text $'Line 1\nLine 2\n indented line'
# Reply inside the thread (message appears in the target thread)
lark-cli im +messages-reply --message-id om_xxx --text "Let's discuss this" --reply-in-thread
# Reply with Markdown containing an image (must pre-upload via images.create)
lark-cli im images create --data '{"image_type":"message"}' --file ./screenshot.png
# Use the returned image_key
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Screenshot\n\n\n\nConfirmed.'
# If you need exact post structure, send JSON directly
lark-cli im +messages-reply --message-id om_xxx --msg-type post --content '{"zh_cn":{"title":"Reply","content":[[{"tag":"text","text":"Detailed content"}]]}}'
# Reply with a local image (uploaded automatically before sending)
lark-cli im +messages-reply --message-id om_xxx --image ./photo.png
# Reply with a local file (uploaded automatically before sending)
lark-cli im +messages-reply --message-id om_xxx --file ./report.pdf
# Reply with a local video (--video-cover is required as the video cover)
lark-cli im +messages-reply --message-id om_xxx --video ./demo.mp4 --video-cover ./cover.png
# With an idempotency key
lark-cli im +messages-reply --message-id om_xxx --text "Received" --idempotency-key my-unique-id
# Preview the request without executing it
lark-cli im +messages-reply --message-id om_xxx --markdown $'## Test\n\nhello' --dry-run
Media Input Rules
- Media flags accept an existing key (
img_xxx/file_xxx), anhttp://orhttps://URL, or a local file path. - Local paths must be relative to the current working directory and stay within it after resolving
..and symlinks. - Absolute paths such as
/tmp/photo.pngare rejected. Run the command from the file's directory and pass./photo.png, or copy the file into the current directory first.
Parameters
| Parameter | Required | Description |
|---|---|---|
--message-id <id> |
Yes | ID of the message being replied to (om_xxx) |
--msg-type <type> |
No | Message type (default text). If you use --text / --markdown / media flags, the effective type is inferred automatically. Explicitly setting a conflicting --msg-type fails validation |
--content <json> |
One content option | Exact reply content as JSON. The JSON must match the effective --msg-type |
--text <string> |
One content option | Plain text reply. Use when exact text and formatting preservation matter |
--markdown <string> |
One content option | Best default for lightweight formatted replies such as headings, lists, links, summaries, and investigation notes. Internally converted to post JSON with Feishu-specific normalization |
--image <path|url|key> |
One content option | Cwd-relative local image path, URL, or image_key (img_xxx) |
--file <path|url|key> |
One content option | Cwd-relative local file path, URL, or file_key (file_xxx) |
--video <path|url|key> |
One content option | Cwd-relative local video path, URL, or file_key (file_xxx); must be used together with --video-cover |
--video-cover <path|url|key> |
Required with --video |
Cwd-relative local cover image path, URL, or image_key (img_xxx) |
--audio <path|url|key> |
One content option | Cwd-relative local audio path, URL, or file_key (file_xxx) |
--reply-in-thread |
No | Reply inside the thread. The reply appears in the target message's thread instead of the main chat stream |
--idempotency-key <key> |
No | Idempotency key; the same key sends only one reply within 1 hour |
--as <identity> |
No | Identity type: bot or user (default bot) |
--dry-run |
No | Print the request only, do not execute it |
Mutual exclusivity rule:
--text,--markdown,--content, and--image/--file/--video/--audiocannot be used together. Media flags are also mutually exclusive with each other.Video cover rule:
--videomust be accompanied by--video-cover. Omitting--video-coverwhen using--videowill fail validation.--video-covercannot be used without--video.
Common Mistakes
- Choosing
--textfor headings, lists, links, summaries, or investigation notes. Use--markdown. - Choosing
--markdownwhen you actually need exact plain text. If exact line breaks, spacing, logs, code, or literal Markdown characters matter, use--text, usually with$'...'. - Assuming
--markdownsupports every Markdown feature. It is converted into a Feishupostpayload and normalized first. - Putting local image paths inside Markdown like
.--markdowndoes not auto-upload those paths. - Using local file paths inside Markdown image syntax (e.g.
) with--markdown. Local paths are not auto-uploaded and will not render as an image. Pre-upload viaimages.createto get animage_keyinstead. - Using
--contentwithout making the JSON match the effective--msg-type. - Explicitly setting
--msg-typeto something that conflicts with--text,--markdown, or media flags. - Mixing
--text,--markdown, or--contentwith media flags in one command.
Return Value
{
"message_id": "om_xxx",
"chat_id": "oc_xxx",
"create_time": "1234567890"
}
Usage Scenarios
Scenario 1: Reply in the main chat stream
lark-cli im +messages-reply --message-id om_xxx --text "OK, I will handle it"
The reply appears in the main chat stream and references the target message.
Scenario 2: Reply inside a thread
lark-cli im +messages-reply --message-id om_xxx --text "Let me take a look at this" --reply-in-thread
The reply appears in the target message's thread and does not show up in the main chat stream.
@Mention Format (text / post)
- Recommended format:
<at user_id="ou_xxx">name</at> - @all:
<at user_id="all"></at> - The shortcut normalizes common variants like
<at id=...>and<at open_id=...>intouser_id, butuser_idremains the recommended documented form
Notes
--message-idmust be a valid message ID inom_xxxformat--contentmust be valid JSON- When using
--content, you are responsible for making the JSON structure match the effectivemsg_type --reply-in-threadaddsreply_in_thread=trueto the API request--reply-in-threadis mainly meaningful in chats that support thread replies--image/--file/--video/--audio/--video-coversupport existing keys, URLs, and cwd-relative local file paths; the shortcut uploads local paths and URLs first, then sends the reply; both the upload and send steps use the same identity (UAT when--as user, TAT when--as bot)- If the provided media value starts with
img_orfile_, it is treated as an existing key and used directly --markdownalways sendsmsg_type=post- If you explicitly set
--msg-typeand it conflicts with the chosen content flag, validation fails - When using
--video,--video-coveris required as the video cover --dry-runuses placeholder image keys for remote Markdown images and placeholder media keys for local uploads- Failures return error codes and messages
--as useruses a user access token (UAT) and requires theim:message.send_as_userandim:messagescopes; the reply is sent as the authorized end user--as botuses a tenant access token (TAT), and requires theim:message:send_as_botscope- When using
--markdownwith images, pre-uploading viaimages.createto obtain animage_keyis recommended for reliability; remote URLs may be auto-resolved at runtime, but if download/upload fails the image is removed with a warning; local paths are not supported