Skip to content

feat(mail): add --page-token and --page-size to mail +triage#301

Open
haidaodashushu wants to merge 1 commit intomainfrom
feat/mail-triage-pagination
Open

feat(mail): add --page-token and --page-size to mail +triage#301
haidaodashushu wants to merge 1 commit intomainfrom
feat/mail-triage-pagination

Conversation

@haidaodashushu
Copy link
Copy Markdown
Collaborator

@haidaodashushu haidaodashushu commented Apr 7, 2026

Summary

  • Add --page-token and --page-size flags to mail +triage for external pagination control
  • Token carries search: or list: prefix to identify API path, with strict validation (conflicting params fail fast, bare tokens rejected)
  • JSON/data output now returns { messages, total, has_more, page_token } object; table shows next-page hint on stderr
  • Update skill reference doc with new flags, output format, and usage examples

Test plan

  • Unit tests for resolveTriagePath: search/list prefix routing, bare token rejection, conflict detection
  • Unit tests for resolveTriagePageSize: --page-size priority over --max, clamping
  • DryRun tests: page-token passed to API params, path selection with prefixes, error cases
  • Manual E2E: list path pagination (10+5+10+10+5=40) matches --max 40 baseline
  • Manual E2E: search path pagination (10+10+10=30) matches --query "周报" --max 30 baseline
  • Manual E2E: search token continuation without --query works
  • All existing mail package tests pass (go test ./shortcuts/mail/...)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added --page-size flag to control results per page
    • Added --page-token flag for pagination continuation
    • JSON/data output now includes pagination metadata (total, has_more, page_token)
    • Table format displays next page command hints for additional results
  • Documentation

    • Updated mail triage documentation with pagination examples and revised output format

…il +triage

Support external pagination for mail +triage with two new flags:
- --page-token: resume from a previous response's page token
- --page-size: alias for --max

Token carries a "search:" or "list:" prefix to identify the API path,
with strict validation: conflicting parameters (e.g. list: token with
--query) fail fast, and bare tokens without prefix are rejected.

JSON/data output now returns an object with messages, total, has_more,
and page_token fields. Table output shows next-page hint on stderr.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

The PR introduces pagination support for the mail +triage command with new --page-size and --page-token flags, token-aware routing to select between search and list APIs based on token prefix (search: or list:), validation logic for token/query conflicts, and restructured JSON output that includes pagination metadata (total, has_more, page_token).

Changes

Cohort / File(s) Summary
Pagination Logic Implementation
shortcuts/mail/mail_triage.go
Added new --page-size and --page-token flags; introduced resolveTriagePath() function for token-aware API routing based on prefix; implemented continuation token handling with prefix stripping (search: and list:); restructured JSON/data output to wrap messages in an object with pagination metadata; modified table output to show next-page hints via stderr.
Pagination Test Coverage
shortcuts/mail/mail_triage_test.go
Added comprehensive unit tests for resolveTriagePageSize() (defaults, precedence, clamping), resolveTriagePath() (token validation, prefix handling, query/filter conflict detection), DryRun behavior with pagination parameters, and flag surface validation for new pagination flags.
Documentation Updates
skills/lark-mail/references/lark-mail-triage.md
Updated JSON/data output examples to reflect new object-wrapped structure with pagination fields; documented --page-token and --page-size parameters; clarified token prefix requirements and precedence rules; adjusted jq examples to reference .messages[] path.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MailTriage as Mail Triage Command
    participant PathResolver as Path Resolver
    participant SearchAPI as Search API
    participant ListAPI as List API
    participant Output as Output Formatter

    User->>MailTriage: Execute with --page-token and --query/--filter
    MailTriage->>PathResolver: resolveTriagePath(token, query, filter)
    
    alt Token has "search:" prefix
        PathResolver->>PathResolver: Strip "search:" prefix
        PathResolver->>MailTriage: Return search path
        MailTriage->>SearchAPI: buildSearchParams with stripped token
        SearchAPI->>SearchAPI: Execute search, get results + nextToken
        SearchAPI->>MailTriage: Return messages + nextToken
        MailTriage->>MailTriage: Wrap nextToken as "search:<token>"
    else Token has "list:" prefix
        PathResolver->>PathResolver: Strip "list:" prefix, validate no query
        PathResolver->>MailTriage: Return list path
        MailTriage->>ListAPI: buildListParams with stripped token
        ListAPI->>ListAPI: Execute list, get results + nextToken
        ListAPI->>MailTriage: Return messages + nextToken
        MailTriage->>MailTriage: Wrap nextToken as "list:<token>"
    else No token or empty token
        PathResolver->>PathResolver: Determine path from query presence
        PathResolver->>MailTriage: Return search or list path
    end
    
    MailTriage->>Output: Format with pagination metadata
    Output->>User: Emit messages + total + has_more + page_token
    
    alt has_more is true
        Output->>User: Emit next-page command hint to stderr
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

domain/mail, size/M

Poem

🐰 Hops through pages with graceful ease,
Search and list tokens, if you please!
Wrapped in objects, metadata gleams,
Pagination dreams flow like pristine streams. 🌱

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding two new flags (--page-token and --page-size) to the mail +triage command.
Description check ✅ Passed The PR description covers all required template sections with substantial detail: Summary explains the feature and validation approach; Changes are implicitly covered through Summary; Test Plan is comprehensive with checkboxes and specific test scenarios; Related Issues section is present (marked as None).

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mail-triage-pagination

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 Apr 7, 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.

🧹 Nitpick comments (2)
skills/lark-mail/references/lark-mail-triage.md (1)

103-106: Add language specifier to fenced code block.

The code block is missing a language identifier. Since this shows shell/terminal output, use an appropriate specifier.

-```
+```text
 15 message(s)
 next page: mail +triage --page-token 'list:FfccvoqPd...' ...
 tip: use mail +message --message-id <id> to read full content
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/lark-mail/references/lark-mail-triage.md` around lines 103 - 106, The
fenced code block that currently contains the terminal output starting with "15
message(s)" should include a language specifier to indicate plain text; update
the triple-backtick fence for that block to use a language tag such as "text"
(e.g., change ``` to ```text) so the shell/terminal output is rendered
correctly.
shortcuts/mail/mail_triage_test.go (1)

1052-1060: Duplicate test coverage for bare token rejection.

TestResolveTriagePathBareTokenRejected (lines 1052-1060) and TestPageTokenBareTokenRejected (lines 1097-1105) test the same scenario. Consider consolidating to reduce test duplication.

Also applies to: 1097-1105

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/mail/mail_triage_test.go` around lines 1052 - 1060, Two tests
duplicate the same scenario: TestResolveTriagePathBareTokenRejected and
TestPageTokenBareTokenRejected both assert that a bare token without a prefix
causes an error; consolidate by removing or merging one of them. Keep a single
test (either TestResolveTriagePathBareTokenRejected or
TestPageTokenBareTokenRejected) that calls resolveTriagePath("baretoken123", "",
triageFilter{}) and asserts err != nil and that err.Error() contains "prefix";
update or remove the duplicate test accordingly and ensure any helper names
referenced (resolveTriagePath, TestResolveTriagePathBareTokenRejected,
TestPageTokenBareTokenRejected) are adjusted so there is only one coverage point
for this case.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@shortcuts/mail/mail_triage_test.go`:
- Around line 1052-1060: Two tests duplicate the same scenario:
TestResolveTriagePathBareTokenRejected and TestPageTokenBareTokenRejected both
assert that a bare token without a prefix causes an error; consolidate by
removing or merging one of them. Keep a single test (either
TestResolveTriagePathBareTokenRejected or TestPageTokenBareTokenRejected) that
calls resolveTriagePath("baretoken123", "", triageFilter{}) and asserts err !=
nil and that err.Error() contains "prefix"; update or remove the duplicate test
accordingly and ensure any helper names referenced (resolveTriagePath,
TestResolveTriagePathBareTokenRejected, TestPageTokenBareTokenRejected) are
adjusted so there is only one coverage point for this case.

In `@skills/lark-mail/references/lark-mail-triage.md`:
- Around line 103-106: The fenced code block that currently contains the
terminal output starting with "15 message(s)" should include a language
specifier to indicate plain text; update the triple-backtick fence for that
block to use a language tag such as "text" (e.g., change ``` to ```text) so the
shell/terminal output is rendered correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5898bb3f-2cdc-4b1d-a7ba-a8b3f559a1d3

📥 Commits

Reviewing files that changed from the base of the PR and between 6bc6bb6 and 236249e.

📒 Files selected for processing (3)
  • shortcuts/mail/mail_triage.go
  • shortcuts/mail/mail_triage_test.go
  • skills/lark-mail/references/lark-mail-triage.md

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@236249eb6d70c18d0657df7c2699a353ca975b42

🧩 Skill update

npx skills add larksuite/cli#feat/mail-triage-pagination -y -g

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 7, 2026

Greptile Summary

This PR adds external pagination controls (--page-token, --page-size) to mail +triage. Prefixed tokens (search: / list:) drive path selection and strict conflict detection prevents silent misrouting. The loop-level hasMore / nextPageToken tracking is correct, tests are comprehensive, and the docs are updated.

The one backward-incompatible change to note: --format json and --format data output changed from a flat array to a {messages, total, has_more, page_token} envelope. All remaining findings are P2.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 and do not block correctness.

Core pagination logic (prefix routing, conflict detection, hasMore/nextPageToken tracking, token stripping) is correct and well-covered by tests. The two P2 items are a documented breaking output-format change and a whitespace-query edge case in an already-commented guard.

shortcuts/mail/mail_triage.go — output format change at lines 253-261 and whitespace-query guard at line 897 are worth a second look if backward compatibility is a concern.

Important Files Changed

Filename Overview
shortcuts/mail/mail_triage.go Adds resolveTriagePath, resolveTriagePageSize, initial-token stripping in both loops, and wraps JSON/data output in a pagination envelope; one whitespace-query edge case and a breaking output format change.
shortcuts/mail/mail_triage_test.go Thorough tests for resolveTriagePath, resolveTriagePageSize, DryRun path selection, token stripping, and flag definitions; minor test duplication but no coverage gaps.
skills/lark-mail/references/lark-mail-triage.md Docs updated with new flags, envelope output format, jq examples migrated from array to object access, and table-format stderr pagination hint.

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI
    participant resolveTriagePath
    participant ListAPI as Lark List API
    participant BatchGet as Lark batch_get API

    User->>CLI: mail +triage --page-token list:abc --max 10
    CLI->>resolveTriagePath: (token=list:abc, query empty)
    resolveTriagePath-->>CLI: useSearch=false, err=nil
    CLI->>ListAPI: GET /messages?page_token=abc&page_size=10
    ListAPI-->>CLI: {items, has_more=true, page_token=xyz}
    CLI->>BatchGet: POST /messages/batch_get {message_ids}
    BatchGet-->>CLI: {messages}
    CLI-->>User: {messages, total=10, has_more=true, page_token=list:xyz}
Loading

Reviews (1): Last reviewed commit: "feat(mail): add --page-token and --page-..." | Re-trigger Greptile

Comment on lines 253 to +261
switch outFormat {
case "json", "data":
output.PrintJson(runtime.IO().Out, messages)
outData := map[string]interface{}{
"messages": messages,
"total": len(messages),
"has_more": hasMore,
"page_token": nextPageToken,
}
output.PrintJson(runtime.IO().Out, outData)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Breaking output format for --format json / --format data

The JSON/data output shape changed from a bare array to a {messages, total, has_more, page_token} envelope. Any existing script piping through jq '.[].subject' or .[0].from will silently produce a type error at runtime. The skill doc is updated, but downstream consumers have no in-band deprecation signal.

If backward compat is needed for a transition period, consider keeping --format data as the flat-array mode (its original pipe-friendliness purpose) and using the new envelope for --format json only.

Comment on lines +896 to +901
case strings.HasPrefix(pageToken, "search:"):
if !paramWantsSearch && (query != "" || len(triageQueryFilterFields(filter)) > 0) {
// This shouldn't normally happen (query/search fields → paramWantsSearch=true),
// but guard against future changes.
return false, fmt.Errorf("--page-token has search: prefix but current --query/--filter parameters indicate list path; remove conflicting parameters or use the correct token")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Whitespace-query edge case in the search: prefix guard

The condition query != "" is not equivalent to strings.TrimSpace(query) != "" used inside usesTriageSearchPath. A caller passing --page-token search:xyz --query " " (whitespace-only query) will hit this branch — paramWantsSearch is false (trimmed query is empty) while query != "" is true — and get a misleading error claiming the params indicate the list path, even though search: is valid for a continuation. Aligning the guard with the same trim logic closes the gap:

Suggested change
case strings.HasPrefix(pageToken, "search:"):
if !paramWantsSearch && (query != "" || len(triageQueryFilterFields(filter)) > 0) {
// This shouldn't normally happen (query/search fields → paramWantsSearch=true),
// but guard against future changes.
return false, fmt.Errorf("--page-token has search: prefix but current --query/--filter parameters indicate list path; remove conflicting parameters or use the correct token")
}
if !paramWantsSearch && (strings.TrimSpace(query) != "" || len(triageQueryFilterFields(filter)) > 0) {

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/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant