Skip to content

feat(mail): add +rule-reorder shortcut with slot-replacement auto-fill#979

Open
bubbmon233 wants to merge 2 commits into
larksuite:mainfrom
bubbmon233:feat/648e851
Open

feat(mail): add +rule-reorder shortcut with slot-replacement auto-fill#979
bubbmon233 wants to merge 2 commits into
larksuite:mainfrom
bubbmon233:feat/648e851

Conversation

@bubbmon233
Copy link
Copy Markdown
Collaborator

@bubbmon233 bubbmon233 commented May 20, 2026

Summary

Adds a new lark-cli mail +rule-reorder shortcut that lets users specify only the rules they want to reorder. The CLI auto-fills the remaining rules from the current server-side order using a slot-replacement algorithm, then calls the Reorder API with the complete merged list.

Problem: ReorderUserMailboxRule API requires all rule IDs. Providing a partial list causes an API error.

Solution: The CLI fetches the current rule order first (List API), merges the user-supplied partial order via slot-replacement, then submits the full merged list.

Slot-Replacement Algorithm

Given current order [D,A,G,C,B,E] and user input E,A:

  1. Find the positions (slots) of user-specified IDs in the current order: A→index 1, E→index 5
  2. Fill those slots with user IDs in the order specified: slot[1]=E, slot[5]=A
  3. Result: [D,E,G,C,B,A]

Changes

File Type Description
shortcuts/mail/mail_rule_reorder.go New MailRuleReorder Shortcut + helpers
shortcuts/mail/shortcuts.go Modified Register MailRuleReorder in Shortcuts()
skills/lark-mail/references/lark-mail-rule-reorder.md New AI reference manual for +rule-reorder

Flags

Flag Required Default Description
--mailbox No "me" Mailbox address; "me" resolves to the caller's own mailbox
--rule-ids Yes* Comma-separated rule IDs in the desired target order

*Validated in the Validate callback (not Required: true), so --dry-run works without providing real IDs.

Auth & Scope

  • Auth types: user, bot
  • Scope: mail:user_mailbox.rule:write

Testing

# Dry-run (shows intent without executing writes)
lark-cli mail +rule-reorder --rule-ids "rule-id-1,rule-id-2" --dry-run

# Reorder two specific rules, auto-fill the rest
lark-cli mail +rule-reorder --rule-ids "rule-id-1,rule-id-2"

# Operate on a different mailbox
lark-cli mail +rule-reorder --mailbox user@example.com --rule-ids "rule-id-1"

Summary by CodeRabbit

  • New Features

    • Added mail +rule-reorder CLI shortcut to reorder inbox rules using a comma-separated list (validates IDs, rejects duplicates, supports dry-run preview).
    • Added top-level skill command with a reference subcommand to print skill reference documents.
  • Documentation

    • Added a usage guide for the mail rule reorder command with examples, constraints, and safety notes.

Review Change Stack

@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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

Adds a new mail +rule-reorder CLI shortcut that validates user-provided rule IDs, fetches current mailbox rules, merges user IDs into current positions via slot-replacement, and calls the Reorder API. Also adds a skill reference command and documentation for the reorder shortcut.

Changes

Mail Rule Reorder Feature

Layer / File(s) Summary
Shortcut implementation and wiring
shortcuts/mail/mail_rule_reorder.go, shortcuts/mail/shortcuts.go
MailRuleReorder shortcut: validates --rule-ids (non-empty, no duplicates), supports --mailbox/--dry-run, lists current rules, ensures provided IDs exist, computes slot-replacement merge, POSTs reorder, and prints final order. Includes helpers to parse/validate IDs and extract IDs from API responses.
User documentation
skills/lark-mail/references/lark-mail-rule-reorder.md
Adds a reference guide with usage examples, parameters, the slot-replacement algorithm, validation rules, return value examples, typical scenarios, constraints, and related commands.

Skill command and wiring

Layer / File(s) Summary
Skill CLI and wiring
cmd/skill/skill.go, cmd/build.go
Adds a top-level skill command with reference subcommand that reads skills/<skill>/references/<name>.md from candidate dirs (env override supported) with path-sanitization and error handling; registers the command in CLI build.

Sequence Diagram

sequenceDiagram
  participant User
  participant CLI as lark-cli
  participant API as MailboxRulesAPI
  User->>CLI: run `mail +rule-reorder --rule-ids X,Y,Z`
  CLI->>API: GET /mail/v1/mailboxes/{mailbox}/rules (list current)
  API->>CLI: current rule list [A,B,C,D,E]
  CLI->>CLI: validate X,Y,Z exist in current
  CLI->>CLI: slot-replace -> merged order [X,Y,Z,B,E]
  CLI->>API: POST /mail/v1/mailboxes/{mailbox}/rules/reorder with merged IDs
  API->>CLI: success { rule_ids: [...], meta.count: 5 }
  CLI->>User: print reordered list and count
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

domain/mail, size/M

Suggested reviewers

  • chanthuang
  • infeng

Poem

🐰 I hop through rules with careful care,
I slot your IDs and leave the rest where,
A GET, a merge, a single POST—hip hooray!
The mailbox reorders without dismay,
✨ A tiny rabbit script to save your day.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary change: adding a new mail shortcut that uses slot-replacement auto-fill for rule reordering.
Description check ✅ Passed The description includes all required sections (Summary, Changes, Test Plan, Related Issues) with comprehensive details about the feature, algorithm explanation, and testing examples.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added domain/mail PR touches the mail domain size/M Single-domain feat or fix with limited business impact labels May 20, 2026
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: 2

🤖 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/mail_rule_reorder.go`:
- Around line 43-49: DryRun currently only records the GET of rules; update the
DryRun lambda (the DryRun function that returns common.NewDryRunAPI) to also
record the intended reorder write by chaining a POST to mailboxPath(mailboxID,
"rules/reorder") so the dry-run shows the write path and payload; include the
same merged/user rule ids from runtime (e.g., runtime.Str("rule-ids") or the
appropriate merged id key) in the POST recording (using the DryRun API's
Set/Body method) so users can preview the reorder request alongside the GET.

In `@skills/lark-mail/references/lark-mail-rule-reorder.md`:
- Line 120: The document contains a typo: "竟态窗口" should be corrected to "竞态窗口";
update the text in skills/lark-mail/references/lark-mail-rule-reorder.md where
the sentence contains "List-Reorder 竟态窗口" (or the exact phrase "不要假设返回的
`rule_ids` 顺序就是\"最终服务端顺序\" — 极小概率存在 List-Reorder 竟态窗口(建议重跑确认)") to replace "竟态"
with "竞态" so the sentence reads "List-Reorder 竞态窗口".
🪄 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: 37012e67-c570-4a74-8f2f-04adbc5dcbd7

📥 Commits

Reviewing files that changed from the base of the PR and between d793790 and 4a410c5.

📒 Files selected for processing (3)
  • shortcuts/mail/mail_rule_reorder.go
  • shortcuts/mail/shortcuts.go
  • skills/lark-mail/references/lark-mail-rule-reorder.md

Comment on lines +43 to +49
DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
mailboxID := resolveMailboxID(runtime)
return common.NewDryRunAPI().
Desc("Step 1: list rules; Step 2: apply slot-replacement fill; Step 3: reorder with merged IDs").
GET(mailboxPath(mailboxID, "rules")).
Set("user_rule_ids", runtime.Str("rule-ids"))
},
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

Dry-run does not preview the reorder write request.

DryRun only records the list (GET) step. It currently misses the reorder (POST .../rules/reorder) intent, so users cannot preview the write operation path/payload in dry-run mode.

💡 Suggested fix
 	DryRun: func(ctx context.Context, runtime *common.RuntimeContext) *common.DryRunAPI {
 		mailboxID := resolveMailboxID(runtime)
 		return common.NewDryRunAPI().
 			Desc("Step 1: list rules; Step 2: apply slot-replacement fill; Step 3: reorder with merged IDs").
 			GET(mailboxPath(mailboxID, "rules")).
-			Set("user_rule_ids", runtime.Str("rule-ids"))
+			Set("user_rule_ids", runtime.Str("rule-ids")).
+			POST(mailboxPath(mailboxID, "rules", "reorder")).
+			Set("rule_ids", []string{"<merged IDs from step 2>"})
 	},
🤖 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_rule_reorder.go` around lines 43 - 49, DryRun currently
only records the GET of rules; update the DryRun lambda (the DryRun function
that returns common.NewDryRunAPI) to also record the intended reorder write by
chaining a POST to mailboxPath(mailboxID, "rules/reorder") so the dry-run shows
the write path and payload; include the same merged/user rule ids from runtime
(e.g., runtime.Str("rule-ids") or the appropriate merged id key) in the POST
recording (using the DryRun API's Set/Body method) so users can preview the
reorder request alongside the GET.

- 不要传入邮箱中不存在的规则 ID — CLI 会在 Execute 阶段报 validation 错误
- 不要传入重复的规则 ID — Validate 阶段即报错
- 不要省略 `--rule-ids` — 该参数为必填,缺少时返回 validation 错误
- 不要假设返回的 `rule_ids` 顺序就是"最终服务端顺序" — 极小概率存在 List-Reorder 竟态窗口(建议重跑确认)
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

修正文案错别字(竟态 → 竞态)。

这里应为“竞态窗口”,当前“竟态”是错别字。

✏️ Suggested fix
-- 不要假设返回的 `rule_ids` 顺序就是"最终服务端顺序" — 极小概率存在 List-Reorder 竟态窗口(建议重跑确认)
+- 不要假设返回的 `rule_ids` 顺序就是"最终服务端顺序" — 极小概率存在 List-Reorder 竞态窗口(建议重跑确认)
🤖 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-rule-reorder.md` at line 120, The
document contains a typo: "竟态窗口" should be corrected to "竞态窗口"; update the text
in skills/lark-mail/references/lark-mail-rule-reorder.md where the sentence
contains "List-Reorder 竟态窗口" (or the exact phrase "不要假设返回的 `rule_ids`
顺序就是\"最终服务端顺序\" — 极小概率存在 List-Reorder 竟态窗口(建议重跑确认)") to replace "竟态" with "竞态"
so the sentence reads "List-Reorder 竞态窗口".

@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

❌ Patch coverage is 14.58333% with 123 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.44%. Comparing base (ed9eecf) to head (0325237).
⚠️ Report is 39 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/mail/mail_rule_reorder.go 0.00% 87 Missing ⚠️
cmd/skill/skill.go 38.18% 33 Missing and 1 partial ⚠️
cmd/build.go 0.00% 1 Missing ⚠️
shortcuts/mail/shortcuts.go 0.00% 1 Missing ⚠️

❌ Your patch check has failed because the patch coverage (14.58%) is below the target coverage (60.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #979      +/-   ##
==========================================
+ Coverage   65.91%   67.44%   +1.52%     
==========================================
  Files         520      577      +57     
  Lines       49277    54413    +5136     
==========================================
+ Hits        32483    36701    +4218     
- Misses      14022    14669     +647     
- Partials     2772     3043     +271     

☔ 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.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 20, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@03252373cc9a4e270a9c3f70073b661707d0bcb1

🧩 Skill update

npx skills add bubbmon233/cli#feat/648e851 -y -g

…nce command

BUG-1: extractRuleIDs in shortcuts/mail/mail_rule_reorder.go was accessing
resp["data"]["items"] but CallAPI/HandleApiResult already unwraps the "data"
envelope, so resp is directly {"items": [...]}. Remove the extra "data" layer
so the function correctly reads resp["items"].

BUG-2: add `lark-cli skill reference <skill-name> --name <doc-name>` command
that prints the contents of skills/<skill-name>/references/<doc-name>.md to
stdout. Binary location is used to resolve the skills directory (checks
<binary_dir>/skills/ and <binary_dir>/../skills/ in order). Supports
LARKSUITE_CLI_SKILLS_DIR env override for test environments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels May 20, 2026
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: 2

🤖 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 `@cmd/skill/skill.go`:
- Around line 41-45: Replace the bare fmt.Errorf returned from the RunE function
in skill.go with the command error wrapper used across CLI commands (use
output.Errorf or output.ErrWithHint) so stderr stays machine-parseable;
specifically, in the RunE closure where you check `if name == ""` return
output.Errorf("--name is required") (or output.ErrWithHint with an additional
hint string), and ensure the output package is imported/used consistently in
this file and any callers of the RunE error behavior.
- Around line 69-74: relPath and fullPath are built from untrusted CLI inputs
and then read with os.ReadFile; replace this with path validation and the repo
VFS: call validate.SafeInputPath on the user-derived path (e.g.,
validate.SafeInputPath(relPath) or on skillName/name before building relPath)
and skip the candidate when validation fails, then use the project's virtual FS
API (vfs.ReadFile or the repo's vfs.* equivalent) instead of os.ReadFile to
open/read fullPath; keep the loop over candidateDirs() and use the same
relPath/fullPath symbols so the change is localized to validation and replacing
os.ReadFile with vfs.ReadFile.
🪄 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: 40d67347-2de7-4b0a-b402-765b6f14d1db

📥 Commits

Reviewing files that changed from the base of the PR and between 4a410c5 and 0325237.

📒 Files selected for processing (3)
  • cmd/build.go
  • cmd/skill/skill.go
  • shortcuts/mail/mail_rule_reorder.go

Comment thread cmd/skill/skill.go
Comment on lines +41 to +45
RunE: func(cmd *cobra.Command, args []string) error {
skillName := args[0]
if name == "" {
return fmt.Errorf("--name is required")
}
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

Use command error wrappers in RunE instead of fmt.Errorf.

Line 44 should return output.Errorf (or output.ErrWithHint) so stderr remains machine-parseable for AI agents.

As per coding guidelines: "cmd/**/*.go: RunE functions in commands must return output.Errorf / output.ErrWithHint — never bare fmt.Errorf, because AI agents parse stderr as JSON".

🤖 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 `@cmd/skill/skill.go` around lines 41 - 45, Replace the bare fmt.Errorf
returned from the RunE function in skill.go with the command error wrapper used
across CLI commands (use output.Errorf or output.ErrWithHint) so stderr stays
machine-parseable; specifically, in the RunE closure where you check `if name ==
""` return output.Errorf("--name is required") (or output.ErrWithHint with an
additional hint string), and ensure the output package is imported/used
consistently in this file and any callers of the RunE error behavior.

Comment thread cmd/skill/skill.go
Comment on lines +69 to +74
relPath := filepath.Join("skills", skillName, "references", name+".md")

for _, dir := range candidateDirs() {
fullPath := filepath.Join(dir, relPath)
data, err := os.ReadFile(fullPath)
if err == nil {
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

Validate and read user-derived paths via validate.SafeInputPath + vfs.*.

fullPath is built from untrusted CLI args (skillName, name) and then read with os.ReadFile. This bypasses required path validation and the repo’s filesystem abstraction.

As per coding guidelines: "Use vfs.* instead of os.* for all filesystem access" and "Validate paths before reading with validate.SafeInputPath because CLI arguments are untrusted (they come from AI agents)".

🤖 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 `@cmd/skill/skill.go` around lines 69 - 74, relPath and fullPath are built from
untrusted CLI inputs and then read with os.ReadFile; replace this with path
validation and the repo VFS: call validate.SafeInputPath on the user-derived
path (e.g., validate.SafeInputPath(relPath) or on skillName/name before building
relPath) and skip the candidate when validation fails, then use the project's
virtual FS API (vfs.ReadFile or the repo's vfs.* equivalent) instead of
os.ReadFile to open/read fullPath; keep the loop over candidateDirs() and use
the same relPath/fullPath symbols so the change is localized to validation and
replacing os.ReadFile with vfs.ReadFile.

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/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants