Skip to content

feat(mail): HTML lint library + Larksuite-native autofix + lark-mail …#1019

Open
bubbmon233 wants to merge 2 commits into
larksuite:mainfrom
bubbmon233:feat/mail-html-lint
Open

feat(mail): HTML lint library + Larksuite-native autofix + lark-mail …#1019
bubbmon233 wants to merge 2 commits into
larksuite:mainfrom
bubbmon233:feat/mail-html-lint

Conversation

@bubbmon233
Copy link
Copy Markdown
Collaborator

@bubbmon233 bubbmon233 commented May 21, 2026

Summary

为 lark-cli mail 域写信链路引入 HTML lint 能力:邮件 HTML 在进入投递前完成安全过滤与 Larksuite-native
格式适配,提升邮件的安全性、兼容性与渲染一致性。

Changes

  • 新增 HTML lint 库(shortcuts/mail/lint/):四档分类(pass / native-autofix / warn-autofix /
    error-strip),覆盖 script / iframe / on* 事件处理器 / 危险 URL scheme 等 XSS
    向量,并将段落、列表、引用等自动修复为 Larksuite mail-editor 原生格式
  • 新增 +lint-html 只读预检 shortcut,供 AI / 用户 / CI 在写信前预览 lint 结果
  • 6 个 compose shortcut(+send / +draft-create / +draft-edit / +reply / +reply-all /
    +forward)在构造 eml 前强制执行 lint;默认 envelope 对 lint 改动透明,--show-lint-details 输出
    lint_applied[] / original_blocked[] 取证
  • compose shortcut 新增 --body-file 参数,支持从文件读取 HTML body(32MB 上限)
  • 新增 5 套 Larksuite-native HTML 邮件模板:资讯周报 / 个人周报 / 团队周报 / 调研报告 / 求职简历
  • 新增 lark-mail skill 文档:references/lark-mail-html.md(HTML
    写法指南)、references/lark-mail-lint-html.md+lint-html 用法)

Test Plan

  • Unit tests pass — lint 库四档规则单测 + 6 个 compose shortcut lint 集成测试
  • go build ./...go test ./shortcuts/mail/... 全部通过
  • 本地手动验证 lark mail +send / +draft-create / +lint-html 等命令符合预期(49 + 48 个 CLI
    端到端用例,含真实邮件渲染人工验证)

Related Issues

  • None

Summary by CodeRabbit

  • New Features

    • HTML sanitization added to compose flows (send/reply/reply-all/forward/draft-create/draft-edit) with autofix and optional lint detail reporting
    • New +lint-html command for local pre-checks
    • --body-file support and --show-lint-details flag available across mail commands
    • Several new HTML email templates (job, weekly, newsletter, market report, team/personal)
  • Documentation

    • Comprehensive mail HTML guide, lint-html docs, and updated shortcut references
  • Tests

    • Extensive unit and end-to-end tests for linting and command behavior

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

Walkthrough

Adds a mail HTML lint library with Feishu-native autofixes, integrates writing-path lint into compose shortcuts, introduces shared --body-file support (32MB cap) and a +lint-html preview command, updates compose shortcuts to run the lint after body/signature resolution, and adds tests, docs, and templates.

Changes

HTML Linting for Mail Composition

Layer / File(s) Summary
Lint library data model and classification rules
shortcuts/mail/lint/types.go, shortcuts/mail/lint/rules.go
Defines Severity, Finding, Report and central rule IDs; implements tag/URL-scheme/CSS-property/event-handler classification used by the lint engine.
HTML linting and Feishu-native autofix engine
shortcuts/mail/lint/linter.go
Implements Run() DOM walker: blocked-tag removal, warning-tier rewrites (<font><span>, <center><div>), URL/attribute sanitization, inline-style filtering, and a Feishu-native augmentation pass (list/blockquote/link/para rewrites).
Comprehensive lint tests
shortcuts/mail/lint/linter_test.go
Extensive tests covering allowed/blocked tags, URL/event-handler/style sanitization, autofix rewrites, excerpt truncation, list/paragraph rewrites, and API contract guarantees.
Shared --body-file flag and readers
shortcuts/mail/body_file.go
Adds bodyFileFlag, validateBodyFileMutex, resolveBodyFromFlags, and readBodyFile with a 32MB read cap and validation callback support.
Write-path lint wiring & envelope helpers
shortcuts/mail/mail_lint_writepath.go, shortcuts/mail/mail_lint_writepath_test.go
Adds runWritePathLint, applyLintToEnvelope, non-nil-empty finding helpers, compose/draft hint helpers, and unit + E2E tests validating envelope shapes and raw-EML behavior.
+lint-html shortcut
shortcuts/mail/mail_lint_html.go, shortcuts/mail/mail_lint_html_test.go
Read-only local shortcut that validates --body/--body-file, runs the lint library, returns cleaned_html and optionally warnings/errors with --show-lint-details, plus pretty formatting and tests.
Compose shortcuts integration (write-path lint + body-file)
shortcuts/mail/mail_draft_create.go, shortcuts/mail/mail_send.go, shortcuts/mail/mail_reply.go, shortcuts/mail/mail_reply_all.go, shortcuts/mail/mail_forward.go, shortcuts/mail/mail_draft_edit.go, shortcuts/mail/mail_draft_create_test.go
Each shortcut now supports --body-file, enforces mutual exclusion with --body, resolves effective body via resolveBodyFromFlags, runs write-path lint after final body+signature assembly (excluding quoted original blocks), and emits lint findings conditionally via --show-lint-details; buildRawEMLForDraftCreate signature updated to return lint slices.
Documentation & templates
skill-template/domains/mail.md, skills/lark-mail/* (SKILL.md, references, assets/templates)
Adds mail HTML writing guidelines, +lint-html and write-path lint docs, updates shortcut docs to document --body-file and mutual-exclusion, and adds multiple HTML email templates.
Shortcut registry update
shortcuts/mail/shortcuts.go
Registers the new MailLintHTML shortcut.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • larksuite/cli#538: overlaps in EML construction and compose pipeline changes.
  • larksuite/cli#205: earlier local image/CID resolution changes interacting with final body preparation.

Suggested labels

enhancement

Suggested reviewers

  • chanthuang
  • infeng
  • haidaodashushu

Poem

🐰 I hopped through tags and tangled style,

I chased the scripts and trimmed each file.
Fonts turned to spans, paragraphs to div,
Drafts now tidy, safe to send and live.
Hooray — the mail will render with a smile!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@github-actions github-actions Bot added domain/mail PR touches the mail domain size/XL Architecture-level or global-impact change labels May 21, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@0b8bb1369c6f5dc33f709329afbfa0078b293c00

🧩 Skill update

npx skills add bubbmon233/cli#feat/mail-html-lint -y -g

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Nitpick comments (2)
skill-template/domains/mail.md (1)

343-343: ⚡ Quick win

Inconsistency in shortcut count.

Line 343 describes "compose 5 shortcut" (+send / +draft-create / +reply / +reply-all / +forward), but line 349 mentions 6 shortcuts by additionally including +draft-edit.

The text should clarify whether +draft-edit is considered part of the "compose shortcuts" group or handle it separately, since it edits existing drafts rather than creating new ones.

🤖 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 `@skill-template/domains/mail.md` at line 343, The text is inconsistent about
whether +draft-edit is part of the "compose shortcuts" group; update the copy to
be explicit: either change "compose 5 shortcut" to "compose 6 shortcut" and
include +draft-edit in the list (+send / +draft-create / +reply / +reply-all /
+forward / +draft-edit), or keep "compose 5 shortcut" and move +draft-edit out
of that list with a short clarifier that +draft-edit is a separate edit-op for
existing drafts; update the sentence containing "compose 5 shortcut" and the
nearby line that mentions +draft-edit so both reflect the chosen interpretation.
skills/lark-mail/assets/templates/research--market-report.html (1)

10-34: ⚡ Quick win

<style> block is allowed by mail lint for LarkSuite-native rendering; inline-only is optional.

This template’s <style> (e.g., skills/lark-mail/assets/templates/research--market-report.html lines 10-34) won’t be stripped by +lint-html: the mail lint implementation explicitly treats <style> as “passed through verbatim”, while blocking/removing <script>, <iframe>, external <link>, on* handlers, and javascript: URLs. Converting the classes to inline CSS is only necessary if the HTML must reliably render in non–LarkSuite email clients after forwarding outside the native mail editor.

🤖 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-mail/assets/templates/research--market-report.html` around lines
10 - 34, The <style> block in the template is currently preserved by our mail
lint but may not render in non‑LarkSuite email clients; update the template
(research--market-report.html) to ensure critical styles are applied inline for
portability by converting key CSS rules from the .research-root,
.gradient-header, .card, .stat-row, .stat-card, .player-row, .player-card,
.callout-error, .tbl, .tbl-bug, .badge-*, and pri-p* classes into inline style
attributes on the corresponding HTML elements (keep the existing <style> for
native Lark rendering but add inline styles to elements that must render
correctly when forwarded), making sure visual tokens like background-color,
padding, border-radius, color, font-size, display, and box-shadow are mirrored
inline.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@shortcuts/mail/lint/linter.go`:
- Around line 758-761: In reorderAttrs remove the unused priority variable and
its initialization loop (the lines creating priority := make(map[string]int,
len(order)) and the for i, k := range order { priority[strings.ToLower(k)] = i +
1 }) since priority is never read; delete those lines and, if strings is now
unused, remove the strings.ToLower usage import or its import entry accordingly.
- Around line 1068-1077: The created inner wrapper (variable inner)
unconditionally sets dir="auto" and style="font-size:14px", which overrides
author-specified values; change the construction of inner so it first inspects
the source paragraph node's attributes (e.g., the original p node's Attr) and:
copy any existing dir attribute into inner instead of overriding it, merge style
attributes so existing font-size in the original style is preserved (only add
"font-size:14px" if no font-size is present), and preserve any other style
properties; in short, only apply the defaults for dir and font-size when they
are absent, otherwise reuse the original attributes.
- Around line 610-616: The code only maps margin- and padding- props to their
shorthands; update the switch that inspects prop (the block with
strings.HasPrefix checks that assigns shorthand) to also treat border shorthand
as satisfying border longhands by adding a branch that sets shorthand = "border"
when prop == "border" or strings.HasPrefix(prop, "border-"); this ensures an
existing "border" declaration prevents later autofixes from appending
border-left/right/top/bottom longhands.

In `@shortcuts/mail/mail_draft_edit.go`:
- Around line 38-40: Update the "--patch-file" description to remove the
absolute "MUST" requirement and clarify that incremental/editing operations must
use patch files while full replacements can use the new "--body" or
"--body-file" shortcuts; specifically, in the description associated with the
"patch-file" flag (referencing "set_body" and "set_reply_body"), state that
patch files are required for incremental body edits,
recipient/header/attachment/inline-image edits and for using "set_reply_body",
whereas "--body" and "bodyFileFlag" support full replacements (set_body) as an
alternative; keep guidance to run "--inspect" to check "has_quoted_content" and
"--print-patch-template" for the JSON structure.

In `@shortcuts/mail/mail_forward.go`:
- Around line 78-82: The validation currently only checks shape/mutex via
validateBodyFileMutex using bodyFlag := runtime.Str("body") and bodyFile :=
strings.TrimSpace(runtime.Str("body-file")), but doesn't resolve bodyFile
content before later compose validation; load/resolve the --body-file contents
here (e.g., read the file path validated by runtime.ValidatePath) into the body
variable and pass that resolved body to validateComposeInlineAndAttachments so
the same body value is seen in Validate as in Execute; keep existing calls to
validateBodyFileMutex and then replace the empty-body usage by the loaded body
when invoking validateComposeInlineAndAttachments.

In `@shortcuts/mail/mail_reply_all.go`:
- Around line 77-84: The code only checks that a --body-file path exists
(validateBodyFileMutex) but does not validate the resolved file contents,
allowing an empty file to bypass the required-body check; update the logic in
the handler where bodyFlag, bodyFile, bodyEmpty and hasTemplate are evaluated to
read and trim the contents of bodyFile when bodyFile != "" (similar to the +send
command pattern), set bodyEmpty based on the resolved content (not just the
flag), and return the same validation error via output.ErrValidation when the
resolved body is empty; reuse or extract the same helper used by +send for
resolving/validating file contents (or add a small helper) so compose/Execute
receives the validated, non-empty body string.

In `@shortcuts/mail/mail_reply.go`:
- Around line 76-83: The current check only inspects the --body-file path string
(bodyFile) and bodyFlag text, letting an empty/whitespace file pass; instead
resolve the file contents when a --body-file is provided and validate that
content before accepting it. In the block around bodyFlag :=
runtime.Str("body"), call the same file-read/resolve used by the send path
(i.e., replace or overwrite bodyFlag with the file contents when bodyFile != ""
and runtime.ValidatePath succeeds), then recompute bodyEmpty from the resolved
body text and run validateBodyFileMutex as before; this ensures Validate sees
the loaded reply body (not an empty filename) and prevents Execute from
receiving empty content. Ensure you update references to bodyFlag/bodyEmpty used
in the later Validate/Execute flow (and keep hasTemplate and the
output.ErrValidation check) so the required-body check uses the resolved body
string.

In `@shortcuts/mail/mail_send.go`:
- Around line 80-90: The validation currently treats a non-empty --body-file
path (bodyFile) as satisfying the required-body rule even if the file's contents
are empty/whitespace; after validateBodyFileMutex and after reading/resolving
the actual body content into the variable used to send the email (e.g., the
resolved `body` variable derived from bodyFlag or the file), re-evaluate the
required-body check using that resolved content (instead of bodyFile or
bodyFlag), so replace the bodyEmpty/bodyFile check with a trim/empty check
against the resolved `body` value (and do the same fix for the similar check
around lines 109-119).

In `@skills/lark-mail/references/lark-mail-reply.md`:
- Line 20: The trailing unmatched bold marker in the sentence "编辑邮件内容前 MUST 先用
Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**"
breaks Markdown; edit the line in skills/lark-mail/references/lark-mail-reply.md
to either remove the stray closing "**" or add a matching opening "**" around
the intended text so the bold markup is balanced and renders correctly.

In `@skills/lark-mail/references/lark-mail-send.md`:
- Line 15: Remove the unmatched trailing bold marker by editing the line that
currently reads "编辑邮件内容前 MUST 先用 Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" in
skills/lark-mail/references/lark-mail-send.md; either delete the final "**" or
add the matching opening "**" to correctly wrap the intended bold text so the
Markdown bold markers are balanced.

---

Nitpick comments:
In `@skill-template/domains/mail.md`:
- Line 343: The text is inconsistent about whether +draft-edit is part of the
"compose shortcuts" group; update the copy to be explicit: either change
"compose 5 shortcut" to "compose 6 shortcut" and include +draft-edit in the list
(+send / +draft-create / +reply / +reply-all / +forward / +draft-edit), or keep
"compose 5 shortcut" and move +draft-edit out of that list with a short
clarifier that +draft-edit is a separate edit-op for existing drafts; update the
sentence containing "compose 5 shortcut" and the nearby line that mentions
+draft-edit so both reflect the chosen interpretation.

In `@skills/lark-mail/assets/templates/research--market-report.html`:
- Around line 10-34: The <style> block in the template is currently preserved by
our mail lint but may not render in non‑LarkSuite email clients; update the
template (research--market-report.html) to ensure critical styles are applied
inline for portability by converting key CSS rules from the .research-root,
.gradient-header, .card, .stat-row, .stat-card, .player-row, .player-card,
.callout-error, .tbl, .tbl-bug, .badge-*, and pri-p* classes into inline style
attributes on the corresponding HTML elements (keep the existing <style> for
native Lark rendering but add inline styles to elements that must render
correctly when forwarded), making sure visual tokens like background-color,
padding, border-radius, color, font-size, display, and box-shadow are mirrored
inline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62896bff-36f5-4ce8-b023-048faf73379c

📥 Commits

Reviewing files that changed from the base of the PR and between 56749e7 and 9416637.

📒 Files selected for processing (32)
  • shortcuts/mail/body_file.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/lint/linter_test.go
  • shortcuts/mail/lint/rules.go
  • shortcuts/mail/lint/types.go
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_draft_create_test.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/mail_forward.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_lint_html_test.go
  • shortcuts/mail/mail_lint_writepath.go
  • shortcuts/mail/mail_lint_writepath_test.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/shortcuts.go
  • skill-template/domains/mail.md
  • skills/lark-mail/SKILL.md
  • skills/lark-mail/assets/templates/job-application--resume.html
  • skills/lark-mail/assets/templates/newsletter--weekly-brief.html
  • skills/lark-mail/assets/templates/research--market-report.html
  • skills/lark-mail/assets/templates/weekly--personal-report.html
  • skills/lark-mail/assets/templates/weekly--team-report.html
  • skills/lark-mail/references/lark-mail-draft-create.md
  • skills/lark-mail/references/lark-mail-draft-edit.md
  • skills/lark-mail/references/lark-mail-forward.md
  • skills/lark-mail/references/lark-mail-html.md
  • skills/lark-mail/references/lark-mail-lint-html.md
  • skills/lark-mail/references/lark-mail-reply-all.md
  • skills/lark-mail/references/lark-mail-reply.md
  • skills/lark-mail/references/lark-mail-send.md

Comment on lines +610 to +616
var shorthand string
switch {
case strings.HasPrefix(prop, "margin-"):
shorthand = "margin"
case strings.HasPrefix(prop, "padding-"):
shorthand = "padding"
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Treat border: shorthand as satisfying border longhands.

This helper only expands margin and padding today. If the author already sets border:..., later autofixes still append border-left:..., so the native blockquote rewrite can override user styling even though this path is supposed to preserve it.

Suggested fix
 	switch {
 	case strings.HasPrefix(prop, "margin-"):
 		shorthand = "margin"
 	case strings.HasPrefix(prop, "padding-"):
 		shorthand = "padding"
+	case prop == "border-left" || prop == "border-right" || prop == "border-top" || prop == "border-bottom" ||
+		strings.HasPrefix(prop, "border-left-") || strings.HasPrefix(prop, "border-right-") ||
+		strings.HasPrefix(prop, "border-top-") || strings.HasPrefix(prop, "border-bottom-"):
+		shorthand = "border"
 	}
📝 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
var shorthand string
switch {
case strings.HasPrefix(prop, "margin-"):
shorthand = "margin"
case strings.HasPrefix(prop, "padding-"):
shorthand = "padding"
}
var shorthand string
switch {
case strings.HasPrefix(prop, "margin-"):
shorthand = "margin"
case strings.HasPrefix(prop, "padding-"):
shorthand = "padding"
case prop == "border-left" || prop == "border-right" || prop == "border-top" || prop == "border-bottom" ||
strings.HasPrefix(prop, "border-left-") || strings.HasPrefix(prop, "border-right-") ||
strings.HasPrefix(prop, "border-top-") || strings.HasPrefix(prop, "border-bottom-"):
shorthand = "border"
}
🤖 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/mail/lint/linter.go` around lines 610 - 616, The code only maps
margin- and padding- props to their shorthands; update the switch that inspects
prop (the block with strings.HasPrefix checks that assigns shorthand) to also
treat border shorthand as satisfying border longhands by adding a branch that
sets shorthand = "border" when prop == "border" or strings.HasPrefix(prop,
"border-"); this ensures an existing "border" declaration prevents later
autofixes from appending border-left/right/top/bottom longhands.

Comment on lines +758 to +761
priority := make(map[string]int, len(order))
for i, k := range order {
priority[strings.ToLower(k)] = i + 1
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
from pathlib import Path

path = Path("shortcuts/mail/lint/linter.go")
lines = path.read_text().splitlines()

print("reorderAttrs excerpt:")
for i in range(754, 785 + 1):
    print(f"{i}: {lines[i-1]}")

print("\nOccurrences of 'priority' in shortcuts/mail/lint/linter.go:")
for i, line in enumerate(lines, 1):
    if "priority" in line:
        print(f"{i}: {line}")
PY

Repository: larksuite/cli

Length of output: 1166


Remove the unused priority variable before merge.

reorderAttrs defines priority at lines 758–761, but there are no reads/uses of priority anywhere else in shortcuts/mail/lint/linter.go, so Go will fail to compile with an “declared and not used” error.

Suggested fix
-	priority := make(map[string]int, len(order))
-	for i, k := range order {
-		priority[strings.ToLower(k)] = i + 1
-	}
 	sorted := make([]xhtml.Attribute, 0, len(n.Attr))
 	used := make([]bool, len(n.Attr))
📝 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
priority := make(map[string]int, len(order))
for i, k := range order {
priority[strings.ToLower(k)] = i + 1
}
sorted := make([]xhtml.Attribute, 0, len(n.Attr))
used := make([]bool, len(n.Attr))
🤖 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/mail/lint/linter.go` around lines 758 - 761, In reorderAttrs remove
the unused priority variable and its initialization loop (the lines creating
priority := make(map[string]int, len(order)) and the for i, k := range order {
priority[strings.ToLower(k)] = i + 1 }) since priority is never read; delete
those lines and, if strings is now unused, remove the strings.ToLower usage
import or its import entry accordingly.

Comment on lines +1068 to +1077
// Build inner <div dir="auto" style="font-size:14px">.
inner := &xhtml.Node{
Type: xhtml.ElementNode,
Data: "div",
DataAtom: atom.Div,
Attr: []xhtml.Attribute{
{Key: "dir", Val: "auto"},
{Key: "style", Val: "font-size:14px"},
},
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't force inner paragraph defaults over author-supplied dir or font-size.

If the original <p> already carries dir or a font-size, the fresh inner wrapper still hard-codes dir="auto" and font-size:14px, so the rewrite changes rendering instead of being additive. <p style="font-size:20px" dir="rtl"> is a concrete example that will come out wrong.

Suggested fix
 	inner := &xhtml.Node{
 		Type:     xhtml.ElementNode,
 		Data:     "div",
 		DataAtom: atom.Div,
-		Attr: []xhtml.Attribute{
-			{Key: "dir", Val: "auto"},
-			{Key: "style", Val: "font-size:14px"},
-		},
 	}
+	if readAttr(n, "dir") == "" {
+		inner.Attr = append(inner.Attr, xhtml.Attribute{Key: "dir", Val: "auto"})
+	}
+	if !hasInlineStyleProp(readAttr(n, "style"), "font-size") {
+		inner.Attr = append(inner.Attr, xhtml.Attribute{Key: "style", Val: "font-size:14px"})
+	}
📝 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
// Build inner <div dir="auto" style="font-size:14px">.
inner := &xhtml.Node{
Type: xhtml.ElementNode,
Data: "div",
DataAtom: atom.Div,
Attr: []xhtml.Attribute{
{Key: "dir", Val: "auto"},
{Key: "style", Val: "font-size:14px"},
},
}
// Build inner <div dir="auto" style="font-size:14px">.
inner := &xhtml.Node{
Type: xhtml.ElementNode,
Data: "div",
DataAtom: atom.Div,
}
if readAttr(n, "dir") == "" {
inner.Attr = append(inner.Attr, xhtml.Attribute{Key: "dir", Val: "auto"})
}
if !hasInlineStyleProp(readAttr(n, "style"), "font-size") {
inner.Attr = append(inner.Attr, xhtml.Attribute{Key: "style", Val: "font-size:14px"})
}
🤖 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/mail/lint/linter.go` around lines 1068 - 1077, The created inner
wrapper (variable inner) unconditionally sets dir="auto" and
style="font-size:14px", which overrides author-specified values; change the
construction of inner so it first inspects the source paragraph node's
attributes (e.g., the original p node's Attr) and: copy any existing dir
attribute into inner instead of overriding it, merge style attributes so
existing font-size in the original style is preserved (only add "font-size:14px"
if no font-size is present), and preserve any other style properties; in short,
only apply the defaults for dir and font-size when they are absent, otherwise
reuse the original attributes.

Comment on lines +38 to 40
{Name: "body", Desc: "Full email body for a complete replacement (set_body). Prefer HTML for rich formatting (bold, lists, links); plain text is also supported. Body type is auto-detected. Use --patch-file with set_reply_body when you need to preserve an existing reply/forward quote block; use --body when you want a full body replacement. Mutually exclusive with --body-file. Cannot be combined with --patch-file body ops."},
bodyFileFlag,
{Name: "patch-file", Desc: "Edit entry point for body edits, incremental recipient changes, header edits, attachment changes, or inline-image changes. All body edits MUST go through --patch-file. Two body ops: set_body (full replacement including quote) and set_reply_body (replaces only user-authored content, auto-preserves quote block). Run --inspect first to check has_quoted_content, then --print-patch-template for the JSON structure. Relative path only."},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align --patch-file description with the new --body/--body-file shortcut path.

Line 40 still says “All body edits MUST go through --patch-file”, which now conflicts with the newly added shorthand body replacement flags and can mislead users.

Suggested text update
-		{Name: "patch-file", Desc: "Edit entry point for body edits, incremental recipient changes, header edits, attachment changes, or inline-image changes. All body edits MUST go through --patch-file. Two body ops: set_body (full replacement including quote) and set_reply_body (replaces only user-authored content, auto-preserves quote block). Run --inspect first to check has_quoted_content, then --print-patch-template for the JSON structure. Relative path only."},
+		{Name: "patch-file", Desc: "Edit entry point for advanced body edits, incremental recipient changes, header edits, attachment changes, or inline-image changes. For quick full-body replacement, use --body/--body-file (equivalent to set_body). In patch-file, body ops are set_body (full replacement including quote) and set_reply_body (replaces only user-authored content, auto-preserves quote block). Run --inspect first to check has_quoted_content, then --print-patch-template for the JSON structure. Relative path only."},
📝 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
{Name: "body", Desc: "Full email body for a complete replacement (set_body). Prefer HTML for rich formatting (bold, lists, links); plain text is also supported. Body type is auto-detected. Use --patch-file with set_reply_body when you need to preserve an existing reply/forward quote block; use --body when you want a full body replacement. Mutually exclusive with --body-file. Cannot be combined with --patch-file body ops."},
bodyFileFlag,
{Name: "patch-file", Desc: "Edit entry point for body edits, incremental recipient changes, header edits, attachment changes, or inline-image changes. All body edits MUST go through --patch-file. Two body ops: set_body (full replacement including quote) and set_reply_body (replaces only user-authored content, auto-preserves quote block). Run --inspect first to check has_quoted_content, then --print-patch-template for the JSON structure. Relative path only."},
{Name: "body", Desc: "Full email body for a complete replacement (set_body). Prefer HTML for rich formatting (bold, lists, links); plain text is also supported. Body type is auto-detected. Use --patch-file with set_reply_body when you need to preserve an existing reply/forward quote block; use --body when you want a full body replacement. Mutually exclusive with --body-file. Cannot be combined with --patch-file body ops."},
bodyFileFlag,
{Name: "patch-file", Desc: "Edit entry point for advanced body edits, incremental recipient changes, header edits, attachment changes, or inline-image changes. For quick full-body replacement, use --body/--body-file (equivalent to set_body). In patch-file, body ops are set_body (full replacement including quote) and set_reply_body (replaces only user-authored content, auto-preserves quote block). Run --inspect first to check has_quoted_content, then --print-patch-template for the JSON structure. Relative path only."},
🤖 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/mail/mail_draft_edit.go` around lines 38 - 40, Update the
"--patch-file" description to remove the absolute "MUST" requirement and clarify
that incremental/editing operations must use patch files while full replacements
can use the new "--body" or "--body-file" shortcuts; specifically, in the
description associated with the "patch-file" flag (referencing "set_body" and
"set_reply_body"), state that patch files are required for incremental body
edits, recipient/header/attachment/inline-image edits and for using
"set_reply_body", whereas "--body" and "bodyFileFlag" support full replacements
(set_body) as an alternative; keep guidance to run "--inspect" to check
"has_quoted_content" and "--print-patch-template" for the JSON structure.

Comment thread shortcuts/mail/mail_forward.go
Comment thread shortcuts/mail/mail_reply_all.go
Comment thread shortcuts/mail/mail_reply.go
Comment on lines +80 to +90
bodyFlag := runtime.Str("body")
bodyFile := strings.TrimSpace(runtime.Str("body-file"))
bodyEmpty := strings.TrimSpace(bodyFlag) == ""
if err := validateBodyFileMutex(bodyFlag, bodyFile, runtime.ValidatePath); err != nil {
return err
}
if !hasTemplate && strings.TrimSpace(runtime.Str("subject")) == "" {
return output.ErrValidation("--subject is required; pass the final email subject (or use --template-id)")
}
if !hasTemplate && strings.TrimSpace(runtime.Str("body")) == "" {
return output.ErrValidation("--body is required; pass the full email body (or use --template-id)")
if !hasTemplate && bodyEmpty && bodyFile == "" {
return output.ErrValidation("--body or --body-file is required; pass the full email body (or use --template-id)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Re-check the required-body rule after reading --body-file.

bodyFile != "" only proves a path was passed. If that file is empty or whitespace-only, +send still passes validation and can create a blank message even though the flag contract says body is required when no template is used. Reuse the resolved body value for the required-body check.

Also applies to: 109-119

🤖 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/mail/mail_send.go` around lines 80 - 90, The validation currently
treats a non-empty --body-file path (bodyFile) as satisfying the required-body
rule even if the file's contents are empty/whitespace; after
validateBodyFileMutex and after reading/resolving the actual body content into
the variable used to send the email (e.g., the resolved `body` variable derived
from bodyFlag or the file), re-evaluate the required-body check using that
resolved content (instead of bodyFile or bodyFlag), so replace the
bodyEmpty/bodyFile check with a trim/empty check against the resolved `body`
value (and do the same fix for the similar check around lines 109-119).


## CRITICAL — 发送工作流(必须遵循)

编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix unmatched bold marker.

The line ends with ** without a matching opening marker, resulting in broken Markdown formatting.

📝 Proposed fix
-编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
+**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
📝 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
编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
🤖 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-mail/references/lark-mail-reply.md` at line 20, The trailing
unmatched bold marker in the sentence "编辑邮件内容前 MUST 先用 Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**"
breaks Markdown; edit the line in skills/lark-mail/references/lark-mail-reply.md
to either remove the stray closing "**" or add a matching opening "**" around
the intended text so the bold markup is balanced and renders correctly.


## CRITICAL — 发送工作流(必须遵循)

编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix unmatched bold marker.

The line ends with ** without a matching opening marker, resulting in broken Markdown formatting.

📝 Proposed fix
-编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
+**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
📝 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
编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
🤖 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-mail/references/lark-mail-send.md` at line 15, Remove the
unmatched trailing bold marker by editing the line that currently reads "编辑邮件内容前
MUST 先用 Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" in
skills/lark-mail/references/lark-mail-send.md; either delete the final "**" or
add the matching opening "**" to correctly wrap the intended bold text so the
Markdown bold markers are balanced.

…skill

为 lark-cli mail 域写信链路引入 HTML lint 能力,提升邮件 HTML 的兼容性、
安全性与 Larksuite-native 格式适配。

lint 库(shortcuts/mail/lint/):
- 四档分类:pass / native-autofix / warn-autofix / error-strip
- 安全规则覆盖 script / iframe / on* 事件处理器 / javascript: 及其它
  危险 URL scheme 等 XSS 向量,未知 scheme 一律删除并归 error
- Larksuite-native 格式自动修复:双层 div 段落、原生多级列表结构、
  灰边引用、Larksuite 蓝链接
- cleaned_html 输出确定性稳定(位置索引派生 data-ol-id),便于
  golden-file 测试与缓存

+lint-html 独立预检 shortcut:
- 只读、不调 API、不建草稿,供 AI / 用户 / CI 在写信前预览 lint 结果

写入路径内置 lint(6 个 compose shortcut):
- +send / +draft-create / +draft-edit / +reply / +reply-all / +forward
  在 emlbuilder 之前强制 lint 净化 HTML
- 默认 envelope 对 lint 改动透明(无 lint 字段),保持小巧供 AI 消费;
  --show-lint-details 显式取证返回 lint_applied[] / original_blocked[]
- --body-file 支持从文件读取 body(32MB 上限),与 --body 互斥

预制 HTML 邮件模板(skills/lark-mail/assets/templates/):
- 资讯周报 / 个人周报 / 团队周报 / 调研报告 / 求职简历 5 套
- 按 Larksuite mail-editor 原生格式编写,含正确的多级列表嵌套结构

lark-mail skill 文档:
- references/lark-mail-html.md:邮件 HTML 写法指南(24 个格式 section
  + 颜色调色盘 + URL scheme + 官方模板套用流程)
- references/lark-mail-lint-html.md:+lint-html 用法
- SKILL.md 顶部 CRITICAL 引导
@bubbmon233 bubbmon233 force-pushed the feat/mail-html-lint branch from 9416637 to f01a428 Compare May 21, 2026 10:02
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
shortcuts/mail/mail_draft_edit.go (1)

74-75: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update stale “all body edits via patch-file” guidance in DryRun/template text.

These strings still instruct users to route all body edits through --patch-file, which now contradicts supported behavior and can mislead automation.

Suggested wording update
- Desc("Edit an existing draft without sending it: first call drafts.get(format=raw) to fetch the current EML, parse it into MIME structure, apply either direct flags or the typed patch from patch-file, re-serialize the updated draft, and then call drafts.update. This is a minimal-edit pipeline rather than a full rebuild, so unchanged headers, attachments, and MIME subtrees are preserved where possible. Body edits must go through --patch-file using set_body or set_reply_body ops. It also has no optimistic locking, so concurrent edits to the same draft are last-write-wins.").
+ Desc("Edit an existing draft without sending it: first call drafts.get(format=raw) to fetch the current EML, parse it into MIME structure, apply either direct flags or the typed patch from patch-file, re-serialize the updated draft, and then call drafts.update. This is a minimal-edit pipeline rather than a full rebuild, so unchanged headers, attachments, and MIME subtrees are preserved where possible. For quick full replacement, use --body/--body-file (equivalent to set_body); use --patch-file for advanced/incremental body edits including set_reply_body. It also has no optimistic locking, so concurrent edits to the same draft are last-write-wins.").
...
- "Use --patch-file for ALL body edits and advanced changes (recipients, headers, attachments, inline images)",
+ "Use --body/--body-file for quick full-body replacement; use --patch-file for advanced/incremental body edits and other changes (recipients, headers, attachments, inline images)",

Also applies to: 590-593

🤖 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/mail/mail_draft_edit.go` around lines 74 - 75, Update the stale
description text in mail_draft_edit.go (the Desc string attached to the GET
route for mailboxPath(mailboxID, "drafts", draftID)) to remove or reword the
claim that "Body edits must go through --patch-file" so it matches current
behavior; revise the DryRun/template wording similarly where the same guidance
appears (also applies to the other occurrence around lines 590-593) and ensure
the description still documents that edits preserve unchanged MIME parts and
that the pipeline is minimal-edit/last-write-wins.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@skills/lark-mail/references/lark-mail-draft-edit.md`:
- Around line 13-15: Update page-wide wording to reflect that the new flags
--body and --body-file expand to the set_body op and are mutually exclusive with
--patch-file and with set_body/set_reply_body inside patch files; find and
replace any remaining sentences or examples that assert "body edits must use
--patch-file" (including examples referencing --patch-file only) to instead
state that full-body replacement can be done via --body/--body-file, while
--patch-file remains for preserving quotes or composing finer-grained ops; keep
the CRITICAL note about using the Read tool to consult
references/lark-mail-html.md before editing and ensure cross-references
(set_body, set_reply_body, --patch-file) clearly state their mutual exclusivity
and when to use each option.

In `@skills/lark-mail/references/lark-mail-html.md`:
- Line 18: In the markdown sentence fragment
"**正文长度自适应**:不正文长度,但要求**首屏要见到关键信息**。", fix the typo by replacing "不正文长度" with
"不限制正文长度" so the guidance reads "**正文长度自适应**:不限制正文长度,但要求**首屏要见到关键信息**。; update
that exact sentence in lark-mail-html.md.

In `@skills/lark-mail/references/lark-mail-reply-all.md`:
- Line 16: The line containing "编辑邮件内容前 MUST 先用 Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" has
an extra trailing bold marker; remove the stray trailing "**" (or add a matching
opening "**" if bolding was intended) so the Markdown bold markers are balanced
and the sentence renders correctly.

---

Outside diff comments:
In `@shortcuts/mail/mail_draft_edit.go`:
- Around line 74-75: Update the stale description text in mail_draft_edit.go
(the Desc string attached to the GET route for mailboxPath(mailboxID, "drafts",
draftID)) to remove or reword the claim that "Body edits must go through
--patch-file" so it matches current behavior; revise the DryRun/template wording
similarly where the same guidance appears (also applies to the other occurrence
around lines 590-593) and ensure the description still documents that edits
preserve unchanged MIME parts and that the pipeline is
minimal-edit/last-write-wins.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: db6151c1-f423-453a-83cf-c97df611cadf

📥 Commits

Reviewing files that changed from the base of the PR and between 9416637 and f01a428.

📒 Files selected for processing (32)
  • shortcuts/mail/body_file.go
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/lint/linter_test.go
  • shortcuts/mail/lint/rules.go
  • shortcuts/mail/lint/types.go
  • shortcuts/mail/mail_draft_create.go
  • shortcuts/mail/mail_draft_create_test.go
  • shortcuts/mail/mail_draft_edit.go
  • shortcuts/mail/mail_forward.go
  • shortcuts/mail/mail_lint_html.go
  • shortcuts/mail/mail_lint_html_test.go
  • shortcuts/mail/mail_lint_writepath.go
  • shortcuts/mail/mail_lint_writepath_test.go
  • shortcuts/mail/mail_reply.go
  • shortcuts/mail/mail_reply_all.go
  • shortcuts/mail/mail_send.go
  • shortcuts/mail/shortcuts.go
  • skill-template/domains/mail.md
  • skills/lark-mail/SKILL.md
  • skills/lark-mail/assets/templates/job-application--resume.html
  • skills/lark-mail/assets/templates/newsletter--weekly-brief.html
  • skills/lark-mail/assets/templates/research--market-report.html
  • skills/lark-mail/assets/templates/weekly--personal-report.html
  • skills/lark-mail/assets/templates/weekly--team-report.html
  • skills/lark-mail/references/lark-mail-draft-create.md
  • skills/lark-mail/references/lark-mail-draft-edit.md
  • skills/lark-mail/references/lark-mail-forward.md
  • skills/lark-mail/references/lark-mail-html.md
  • skills/lark-mail/references/lark-mail-lint-html.md
  • skills/lark-mail/references/lark-mail-reply-all.md
  • skills/lark-mail/references/lark-mail-reply.md
  • skills/lark-mail/references/lark-mail-send.md
✅ Files skipped from review due to trivial changes (5)
  • skills/lark-mail/references/lark-mail-draft-create.md
  • skills/lark-mail/references/lark-mail-forward.md
  • skill-template/domains/mail.md
  • skills/lark-mail/references/lark-mail-lint-html.md
  • skills/lark-mail/references/lark-mail-reply.md

Comment on lines +13 to +15
**正文整体替换的快捷方式:** `--body <text>` / `--body-file <path>`(二选一互斥)会自动展开为 `set_body` op。如果只想做整段正文替换且不需要保留引用区,用这两个 flag 即可,无需写 patch-file。要保留引用区或做更精细的 op 组合,仍走 `--patch-file`。两个入口与 `--patch-file` 内的 `set_body` / `set_reply_body` 互斥。

**CRITICAL - 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align page-wide instructions with the new --body/--body-file flow.

These new sections correctly add direct body-edit flags, but other sections in this page still state body editing must go through --patch-file, which creates conflicting operator guidance. Please update the remaining “patch-file only” wording/examples to match this new contract.

Also applies to: 77-78

🤖 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-mail/references/lark-mail-draft-edit.md` around lines 13 - 15,
Update page-wide wording to reflect that the new flags --body and --body-file
expand to the set_body op and are mutually exclusive with --patch-file and with
set_body/set_reply_body inside patch files; find and replace any remaining
sentences or examples that assert "body edits must use --patch-file" (including
examples referencing --patch-file only) to instead state that full-body
replacement can be done via --body/--body-file, while --patch-file remains for
preserving quotes or composing finer-grained ops; keep the CRITICAL note about
using the Read tool to consult references/lark-mail-html.md before editing and
ensure cross-references (set_body, set_reply_body, --patch-file) clearly state
their mutual exclusivity and when to use each option.

- **邮件标题小于50字**: 邮件主题行 `--subject` 应控制在 50 字内,避免超长标题带来理解困难
- **多用列表、表格**:不要堆叠过长的文本段落,请擅长使用列表`<ul>` / `<ol>`或分段 `<p>`
- **列表书写规则**:**不要**用 `<p>一、...</p><p>二、...</p>` 这种「中文编号 + 段落」的列表样式,"①②③"、"1) 2) 3)的机械写法也请摒弃;请擅长使用列表格式 `<ul>` / `<ol>`。
- **正文长度自适应**:不正文长度,但要求**首屏要见到关键信息**。
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix typo in guidance sentence.

Line 18 likely has a typo: “不正文长度” should be “不限制正文长度”.

Suggested fix
-- **正文长度自适应**:不正文长度,但要求**首屏要见到关键信息**。
+- **正文长度自适应**:不限制正文长度,但要求**首屏要见到关键信息**。
📝 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
- **正文长度自适应**不正文长度,但要求**首屏要见到关键信息**
- **正文长度自适应**不限制正文长度,但要求**首屏要见到关键信息**
🤖 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-mail/references/lark-mail-html.md` at line 18, In the markdown
sentence fragment "**正文长度自适应**:不正文长度,但要求**首屏要见到关键信息**。", fix the typo by
replacing "不正文长度" with "不限制正文长度" so the guidance reads
"**正文长度自适应**:不限制正文长度,但要求**首屏要见到关键信息**。; update that exact sentence in
lark-mail-html.md.


## CRITICAL — 发送工作流(必须遵循)

编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix unbalanced Markdown bold marker.

Line 16 has a trailing ** without a matching opening marker, so the critical sentence formatting is broken.

🤖 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-mail/references/lark-mail-reply-all.md` at line 16, The line
containing "编辑邮件内容前 MUST 先用 Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" has
an extra trailing bold marker; remove the stray trailing "**" (or add a matching
opening "**" if bolding was intended) so the Markdown bold markers are balanced
and the sentence renders correctly.

chanthuang
chanthuang previously approved these changes May 21, 2026
Drop the unused `readAttr` helper in shortcuts/mail/lint/linter.go
that was flagged by golangci-lint (unused linter). Apply gofmt to
linter.go and rules.go which had minor formatting issues.
@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

❌ Patch coverage is 85.07135% with 136 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.02%. Comparing base (56749e7) to head (0b8bb13).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/mail/lint/linter.go 94.05% 19 Missing and 15 partials ⚠️
shortcuts/mail/mail_lint_html.go 60.65% 19 Missing and 5 partials ⚠️
shortcuts/mail/mail_draft_create.go 57.14% 12 Missing and 9 partials ⚠️
shortcuts/mail/mail_draft_edit.go 51.61% 10 Missing and 5 partials ⚠️
shortcuts/mail/body_file.go 53.84% 7 Missing and 5 partials ⚠️
shortcuts/mail/mail_send.go 66.66% 4 Missing and 5 partials ⚠️
shortcuts/mail/mail_reply.go 73.91% 3 Missing and 3 partials ⚠️
shortcuts/mail/mail_reply_all.go 73.91% 3 Missing and 3 partials ⚠️
shortcuts/mail/mail_forward.go 80.00% 2 Missing and 2 partials ⚠️
shortcuts/mail/mail_lint_writepath.go 80.95% 2 Missing and 2 partials ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1019      +/-   ##
==========================================
+ Coverage   67.61%   68.02%   +0.41%     
==========================================
  Files         575      581       +6     
  Lines       54347    55223     +876     
==========================================
+ Hits        36747    37567     +820     
- Misses      14555    14565      +10     
- Partials     3045     3091      +46     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with 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.

Inline comments:
In `@shortcuts/mail/lint/linter.go`:
- Around line 488-492: The inline comment documenting the <li> style is out of
sync with the actual autofix output: ensureFeishuListItemStyle emits
margin-top:0px and margin-bottom:0px but the comment shows 4px; update the
comment block for the <li> entry so it matches the real output
(margin-top:0px;margin-bottom:0px) or, if you intended the autofix to emit 4px,
change ensureFeishuListItemStyle instead — reference ensureFeishuListItemStyle
and the <li> style comment in the same file and make them consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f95fd22-aefc-4e82-8301-0edf95bf7b79

📥 Commits

Reviewing files that changed from the base of the PR and between f01a428 and 0b8bb13.

📒 Files selected for processing (2)
  • shortcuts/mail/lint/linter.go
  • shortcuts/mail/lint/rules.go

Comment on lines +488 to +492
// <ol> / <ul> → margin-top:0;margin-bottom:0;margin-left:0;padding-left:0;list-style-position:inside
// <li> → line-height:1.6;margin-top:4px;margin-bottom:4px;padding-left:0;
// margin-left:0;display:list-item;list-style-position:inside;
// list-style-type:decimal (ol parent) | disc (ul parent);
// font-family:inherit;font-size:14px
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep the <li> style comment aligned with the actual autofix output.

Lines 489-492 document margin-top:4px / margin-bottom:4px, but ensureFeishuListItemStyle still emits 0px for both at Lines 919-920. In this renderer-sensitive path, a stale inline contract is easy to cargo-cult into the next change.

🤖 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/mail/lint/linter.go` around lines 488 - 492, The inline comment
documenting the <li> style is out of sync with the actual autofix output:
ensureFeishuListItemStyle emits margin-top:0px and margin-bottom:0px but the
comment shows 4px; update the comment block for the <li> entry so it matches the
real output (margin-top:0px;margin-bottom:0px) or, if you intended the autofix
to emit 4px, change ensureFeishuListItemStyle instead — reference
ensureFeishuListItemStyle and the <li> style comment in the same file and make
them consistent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/mail PR touches the mail domain size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants