* feat(im): support rich-text message attachment zone in send/reply/mget/edit Support the post message attachment zone (top-level files array) end to end: - +messages-send / +messages-reply: repeatable --attachment file_key flags merged into the post content's files array (deduplicated). - +messages-mget: render attachment-zone files/folders as <file>/<folder> tags in content, extract file keys for --download-resources. - +messages-edit: new shortcut (PUT /open-apis/im/v1/messages/:id) with --set-attachments / --clear-attachments; body-only edits preserve the attachment zone by default. - Attachment flags are mutually exclusive with --content carrying a files array (declare the zone via one or the other, not both). - bot-only identity, matching server behavior (user token rejected). - Fixes from review: attachments no longer bypass content mutual-exclusion validation (P1); merge dedups by key. - Docs (SKILL.md, references, affordance) and unit tests updated. * fix(im): address design-review findings (auto-infer post, dedup set, doc routing) - --attachment/--set-attachments/--clear-attachments now infer msg_type=post automatically; only an explicit incompatible --msg-type conflicts. --text is rejected with attachments (text is a standalone message, not a post body) with a hint to use --markdown or --content. - --set-attachments deduplicates repeated keys (docs promised this; the replace helper now enforces it). - Shortcut Description no longer leaks the HTTP path or the raw server error phrase; it describes the command semantically. - affordance/im.md +messages-edit now routes WHEN: interactive cards go to messages.patch, corrected messages go to +messages-send, and attachment tri-state tips are listed. - mget doc no longer claims --format json exposes raw wire fields (the output is the rendered content); download eligibility clarified.
Affordance
Per-command usage guidance for the CLI, authored as one markdown file per domain
(<service>.md). It is surfaced in lark-cli <command> --help and in the
schema output, and read directly at runtime (lazy, cached) — there is no build
step. Maintain these files alongside skills/ and shortcuts/.
Format
A small, fixed markdown subset; each file describes one domain:
# <domain> optional `> skill: <name>` applies to every command below
## Skills optional bullets shown on the business-domain help;
merged after `> skill:` and not inherited by commands
## <command> the command as typed, minus `lark-cli <domain>`; a
+-prefixed heading (## +create) targets that shortcut
<lead paragraph> when to use this command
### Avoid when when not to use it / which command to use instead
### Prerequisites what you must have first (e.g. an id, and where it comes from)
### Tips gotchas and constraints
### Examples **description** lines, each followed by a fenced command
### Skills bullet skill names, or name/relpath references
(lark-contact/references/x.md), to read for usage;
merged with the domain `> skill:` default (deduped,
domain first)
### <other heading> a custom section; flows through verbatim
Reference another command with [[command]] — it renders as command in help.
Under Avoid when it means "use that one instead"; under Prerequisites
("… from command") it means "get the input there first".
Both service-API commands (## messages get) and +-prefixed shortcuts
(## +create) take entries. A ### Skills entry is a skill name (validated
against <name>/SKILL.md) or a name/relpath reference into that skill
(validated against the path); help drops any that don't resolve, so a typo shows
nothing. Point a command at its own reference (e.g. +search-user →
lark-contact/references/lark-contact-search-user.md) rather than re-listing the
domain skill, which the > skill: default already covers. When a shortcut also
sets a hand-authored Tips list in Go, the overlay's ### Tips win — they
replace the Go tips (not merged), so keep tips in one place.
The reserved domain-level ## Skills section controls the skill pointers on
lark-cli <domain> --help. Its entries use the same name or name/relpath format
and existence gating as command-level ### Skills. The canonical > skill: is
shown first automatically; additional domain entries are display-only and do
not become defaults for every command. If ## Skills is absent, the existing
single domain guide behavior is unchanged.
Example
## messages get
Fetch the full content of a single message by id.
### Avoid when
- Reading several at once → use [[messages batch_get]]
### Prerequisites
- message_id from [[messages list]]
### Examples
**Fetch one message**
```bash
lark-cli mail user_mailbox.messages get --message-id "<id>"
```
Notes
- Write plain prose; the only convention is wrapping command references in
[[ ]]. - Treat the lead as decision context, not a second command description. The shortcut or method description stays canonical; omit the lead when it would only restate that description.
- Keep it concise and high-signal — don't restate field/flag names, id types, or anything the schema and flags already show; the agent infers the rest.
- Command-form headings resolve to method ids via the registry, so plural resource
names (
messages) map to the singular method id (message) automatically.+-prefixed shortcut headings are matched verbatim (no plural/space folding), so the heading must equal the shortcut command exactly (## +history-revert).