Skip to content

feat(mail): HTML lint lib + Larksuite-native autofix + lark-mail skill#5

Open
bubbmon233 wants to merge 10 commits into
mainfrom
feat/lark-mail
Open

feat(mail): HTML lint lib + Larksuite-native autofix + lark-mail skill#5
bubbmon233 wants to merge 10 commits into
mainfrom
feat/lark-mail

Conversation

@bubbmon233
Copy link
Copy Markdown
Owner

Summary

Adds an HTML lint library + Larksuite-native autofix to lark-cli mail, plus the skills/lark-mail/ skill bundle (2 reference docs, 5 HTML templates, the +lint-html shortcut, and writing-path lint integration across all 6 compose shortcuts).

What's in this PR

1. Lint library (shortcuts/mail/lint/)

3-tier rule set:

  • Error: drop dangerous tags (<script> / <iframe> / <form> / <input> / <link> / <object> / <embed>), on* event handlers, and javascript: / vbscript: / file: URLs.
  • Warning + autofix: rewrite HTML4-era tags (<font> / <center> / <marquee> / <blink>).
  • Larksuite-native autofix: rewrite <p> / <ul> / <ol> / <li> / <blockquote> / <a> to mail-editor native markup so AI can write the simplest HTML and still produce native-quality rendering.
  • Inline-style and URL-scheme allow-list filtering.
  • <style> block passthrough (server adds CSS scope class).

2. +lint-html shortcut (preview / CI)

Read-only HTML preview tool. Default envelope returns only cleaned_html; --show-lint-details adds full warnings[] / errors[]. --strict exits non-zero on any finding (CI gate).

3. Writing-path lint in the 6 compose shortcuts

+send / +draft-create / +reply / +reply-all / +forward / +draft-edit body op all run lint before drafting:

  • lint_applied_count / original_blocked_count — always present.
  • lint_applied[] / original_blocked[] — only with --show-lint-details.
  • compose_hint — points AI consumers to the HTML writing guide.

4. skills/lark-mail/ skill bundle

  • 5 pre-rendered Larksuite-native HTML templates: weekly newsletter, personal weekly report, team weekly report, market research report, résumé.
  • 2 reference docs:
    • references/lark-mail-html.md — writing rules + format primitives + template-usage flow.
    • references/lark-mail-lint-html.md+lint-html usage + return-value contract + 9 examples.
  • SKILL.md updates linking the new docs and templates.

5. Sealed conventions

Fixed writing conventions enforced by the lint library, the Larksuite mail-editor data model, or the upstream service-side sanitiser.

  • @user mention chipid="at-user-N" is the only hard requirement; do not write data-user-id.
  • Highlight palette — 3 colors (pink milestones, yellow follow-ups, green completed); black text, no bold / padding / border-radius.
  • Brand color palette — main black, 3 levels of grey, Lark blue / deep blue, alert red, emergency orange, light pink / light grey backgrounds, border grey.
  • URL scheme allow-listhttp(s): / mailto: / cid: / data:image/* only.
  • Inline style allow-list — font-* / color / background-color / text-* / line-height / letter-spacing / vertical-align / margin* / padding* / width / height / display / border* / list-style* / white-space / word-break / overflow / transition / cursor / opacity.
  • Tag allow-list<p> / <div> / <span> / <a> / <img> / <table> (with <thead> / <tbody> / <tfoot> / <tr> / <td> / <th> / <caption> / <colgroup> / <col>) / <ul> / <ol> / <li> / <blockquote> / <h1>-<h6> / <b> / <i> / <em> / <strong> / <u> / <s> / <sub> / <sup> / <pre> / <code> / <style>.
  • Writing-style floor — subject ≤ 50 chars; decision-first; lists instead of "一、二、三" / "①②③"; emoji only as status tags; greeting / sign-off ≤ 1 paragraph each.

Tests

  • shortcuts/mail/lint/... — unit tests for every rule.
  • shortcuts/mail/mail_lint_html_test.go+lint-html envelope contract.
  • shortcuts/mail/mail_lint_writepath_test.go — writing-path envelope contract.
  • 5 templates verified via +draft-create smoke test.

Test plan

  • go test ./shortcuts/mail/lint/... ./shortcuts/mail/...
  • All 5 templates render correctly via +draft-create smoke
  • Default vs --show-lint-details envelope verified for both +lint-html and +draft-create

Add an HTML lint library + Larksuite-native autofix to lark-cli mail, plus
the skills/lark-mail/ skill bundle (2 reference docs, 5 HTML templates, the
+lint-html shortcut, and writing-path lint integration across all 6 compose
shortcuts).

Lint library (shortcuts/mail/lint/)

- Error: drop dangerous tags (<script> / <iframe> / <form> / <input> /
  <link> / <object> / <embed>), on* event handlers, javascript: /
  vbscript: / file: URLs.
- Warning + autofix: rewrite HTML4-era <font> / <center> / <marquee> /
  <blink>.
- Larksuite-native autofix: rewrite <p> / <ul> / <ol> / <li> /
  <blockquote> / <a> to mail-editor native markup so AI can write the
  simplest HTML and still produce native-quality rendering.
- Inline-style and URL-scheme allow-list filtering.
- <style> block passthrough (server adds CSS scope class).

+lint-html shortcut (preview / CI)

Read-only HTML preview tool. Default envelope returns only cleaned_html;
--show-lint-details adds full warnings[] / errors[]. --strict exits non-
zero on any finding (CI gate).

Writing-path lint in 6 compose shortcuts

+send / +draft-create / +reply / +reply-all / +forward / +draft-edit body
op all run lint before drafting:

- lint_applied_count / original_blocked_count: always present.
- lint_applied[] / original_blocked[]: only with --show-lint-details.
- compose_hint: points AI consumers to the HTML writing guide.

skills/lark-mail/ skill bundle

5 pre-rendered Larksuite-native HTML templates: weekly newsletter,
personal weekly report, team weekly report, market research report,
résumé.

2 reference docs:
- references/lark-mail-html.md: writing rules + format primitives +
  template-usage flow.
- references/lark-mail-lint-html.md: +lint-html usage + return-value
  contract + 9 worked examples.

SKILL.md updates linking the new docs and templates.

Sealed conventions

- @user mention chip: id="at-user-N" is the only hard requirement; do
  not write data-user-id.
- Highlight palette: 3 colors (pink milestones, yellow follow-ups, green
  completed); black text, no bold / padding / border-radius.
- Brand color palette: main black, 3 levels of grey, Lark blue / deep
  blue, alert red, emergency orange, light pink / light grey
  backgrounds, border grey.
- URL scheme allow-list: http(s): / mailto: / cid: / data:image/* only.
- Inline-style + tag allow-lists.
- Writing-style floor: subject <= 50 chars, decision-first, lists instead
  of mechanical numbering, emoji only as status tags.

Tests

- shortcuts/mail/lint/...: unit tests for every rule.
- shortcuts/mail/mail_lint_html_test.go: +lint-html envelope contract.
- shortcuts/mail/mail_lint_writepath_test.go: writing-path envelope
  contract.
- 5 templates verified via +draft-create smoke test.
Move data["lint_applied_count"] / data["original_blocked_count"]
assignments inside the showDetails branch in applyLintToEnvelope so
all four lint fields enter and leave the envelope together. This
restores the default 3-key envelope (compose_hint / draft_id / reference)
for the compose-family shortcuts (+send / +reply / +reply-all /
+forward / +draft-create / +draft-edit) and keeps the four lint fields
behind --show-lint-details as the tech design intends.

sprint: S2
Remove tip field from buildDraftSavedOutput's returned map and flip the
test assertions in TestBuildDraftSavedOutputIncludesReferenceOnlyWhenPresent
to require tip absence. The compose-family default envelope (+send /
+reply / +reply-all / +forward) now stays within the 3-key contract
(compose_hint / draft_id / reference) defined in tech-design v1 §4.1.5.

hintSendDraft already writes the equivalent guidance to stderr, so no
UX regression — the message reaches the user via the dedicated stderr
hint channel instead of the structured stdout envelope.

sprint: S3
+draft-create now always attaches a fixed draft_edit_hint to its stdout
envelope (alongside compose_hint + draft_id), guiding callers to edit
the existing draft via +draft-edit --draft-id <id> instead of re-running
+draft-create and producing duplicate drafts. The hint is single-target:
only MailDraftCreate emits it; the other 5 compose shortcuts (+send,
+reply, +reply-all, +forward, +draft-edit) keep their existing envelope
shape unchanged.

applyLintToEnvelope no longer writes lint_applied_count or
original_blocked_count. Under --show-lint-details the envelope returns
only the two Finding arrays (lint_applied[] / original_blocked[]) —
callers needing a count compute it via len(arr). The change propagates
to all 6 compose shortcuts via the shared helper.

Tests, the shortcut flag description, and the lark-mail-html /
lark-mail-lint-html reference docs are updated to match.

sprint: S2
…tcut structs (PR 787 followup)

The 4 compose shortcuts (+send, +reply, +reply-all, +forward) were missing
the `HasFormat: true` field in their Shortcut struct literals, so cobra
parse rejected `--format json` with `unknown flag: --format`. This blocked
the JSON envelope output path (compose_hint / lint envelope) that the
verify suite exercises.

The fix mirrors the existing positive siblings in the same package
(mail_draft_create.go:43, mail_draft_edit.go:29, mail_lint_html.go:43):
add a single line `HasFormat:   true,` between `AuthTypes:` and `Flags:`
in each of the 4 Shortcut struct literals. No new manual --format flag
entry is added; the framework auto-registers --format via runner.go when
HasFormat == true.

Refs: verification_report §3.1 (fail_code_bug for INT-CLI-Send-DefaultHidesStrip-01)
+draft-edit only accepted body edits via --patch-file (set_body op),
causing "unknown flag: --body" when called the same way as +draft-create.
This adds --body as a convenience flag that translates directly into a
set_body patch op, making all mail compose shortcuts consistent.

- Add --body flag to MailDraftEdit.Flags
- In buildDraftEditPatch: if --body is set, prepend a set_body op;
  mutual-exclusion check prevents combining with --patch-file body ops
- Update patch template notes to reflect the new --body shorthand
- Existing lint pipeline (Execute loop over ops) already handles the
  new op: HTML is sanitized, envelope hides lint_applied/original_blocked
  by default unless --show-lint-details is passed

Fixes: INT-CLI-DraftEdit-DefaultHidesStrip-01 and dependent cases
Bug 1: Template HTML <li> elements with class "temp-li bullet2" or
"temp-li bullet3" were missing the "bullet1" class required by the
STYLE_LIST_ITEM_NATIVE_INLINE_APPLIED lint rule, causing 18+ warnings
in --strict mode for PersonalWeekly/TeamWeekly/Resume templates.
research--market-report.html used native <li> elements which were
fully converted to Feishu-native list format (ul/ol + li with all
required class, data-* attrs, style props and text span wrapping).

Bug 2: +send lacked --body-file flag, breaking the E2E workflow:
  +lint-html → save cleaned.html → +send --body-file ./cleaned.html
Added --body-file flag (mutually exclusive with --body) that reads
the HTML body from a file. Matches the pattern in +lint-html.
- Add class="not-doclink" to all 20 mention-chip <a id="at-user-N"> elements
  (fixes 20× STYLE_LINK_NATIVE_INLINE_APPLIED warnings in --strict mode)
- Change class="temp-li number2" → "temp-li number1 number2" on the two
  <li data-start="a|b"> sub-items in 下周工作 nested ol
  (fixes 2× STYLE_LIST_ITEM_NATIVE_INLINE_APPLIED warnings)
- Add start="1" to the outer wrapper <ol> of the nested weekly-next sub-list
  (fixes 1× STYLE_LIST_NATIVE_INLINE_APPLIED warning; ensureAttr saw start absent)

All 23 warnings eliminated; +lint-html --strict should now exit 0.
Replace 11 <p> tags with <div> elements (preserving inline styles) to
eliminate STYLE_PARA_WRAPPER_REWRITTEN warnings. The lint engine rewrites
<p> to Lark-native double-wrapped div paragraphs and emits a warning for
each; using <div> directly avoids the rewrite.

Also add class="not-doclink" + native link inline styles to 3 bare <a>
tags in the PR-reference section, eliminating STYLE_LINK_NATIVE_INLINE_APPLIED
warnings.

Verified with lint.Run() directly: blocked=0 applied=0 (0 findings).
All 5 templates now pass +lint-html --strict with 0 findings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant