feat(mail list): server-side unread/flagged filtering, envelope fields, and field selection - #16
Merged
bscott merged 1 commit intoAug 8, 2026
Conversation
…s, field selection Reworks `mail list` per the issue: - ListMessages now takes a ListOptions struct instead of positional args, making it cleaner to extend. - --unread uses IMAP SEARCH UNSEEN and --flagged adds SEARCH FLAGGED, both server-side, so results respect the limit instead of fetching N and filtering client-side (which returned fewer than requested). - MessageSummary gains from_address, to, message_id, and in_reply_to — all already present in the ENVELOPE, so there is no extra fetch cost. in_reply_to makes it possible to reconstruct reply chains from list output. - --fields projects JSON output to a chosen set of keys and --compact emits a bare JSON array; both affect JSON mode only, text output is unchanged. README documents the new flags, fields, and JSON shape. Closes bscott#9 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner
|
Reviewed and verified locally: Merging. Two follow-ups I'm pushing right after:
Also noting for later, not blocking: Closes #9. |
bscott
added a commit
that referenced
this pull request
Aug 8, 2026
#15 and #16 merge cleanly in git but collide semantically: #15's client_affected_test.go calls the pre-#16 ListMessages signature, so master did not build. Ported those three call sites to ListOptions. Also guards the no-match detection added in #15. CopyCommand.Wait() returns &cmd.data unconditionally in go-imap v2, so the `copyData != nil` half of the check was dead code and the result rested entirely on COPYUID. COPYUID is only sent by servers advertising UIDPLUS (folded into IMAP4rev2); without it an empty SourceUIDs/DestUIDs pair means the server never reported, not that nothing was copied — which would have failed every successful copy/label/move. Extracted copyMatchedNothing(), which draws that conclusion only when the capability guarantees the data. #15's fixture pins Caps to IMAP4rev2 and so could not cover this; added a plain IMAP4rev1 fixture. On such a server COPY no-match detection is necessarily weaker, but MoveMessages still catches it via the STORE affected-count — pinned by TestMoveMissingUIDWithoutUIDPlusStillErrors. Finally, adds #16's new mail list flags (--flagged, --fields, --compact) to the --help-json schema per CLAUDE.md, along with --offset and --page, which were already missing. That output is the documented agent contract. Co-authored-by: exe.dev user <exedev@discovery-lotus.exe.xyz> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Aug 8, 2026
bscott
added a commit
that referenced
this pull request
Aug 8, 2026
Carries forward the unique portion of #13. The env-var and mail-list work in that PR landed independently via #14 and #16, so only the batch command remains; this drops the duplicated implementations rather than conflicting with what shipped. mail batch runs multiple label/unlabel/archive/move/flag/delete operations over a single IMAP session, avoiding a connect and auth round-trip per operation. All operations are validated up front, so malformed input fails before any connection is opened or any mailbox is mutated. Names containing IMAP special characters or CRLF are rejected, and input is capped at 10MB. Also fixes two defects from #13: - The --help-json examples used \" inside raw string literals, which emitted literal backslashes into the agent-facing schema. - docs/commands.md listed `seq` as a --fields name; the implementation that shipped in #16 uses `seq_num`. Adds a CHANGELOG covering everything in 0.2.6 and bumps the version. Closes #10. Co-authored-by: exe.dev user <exedev@discovery-lotus.exe.xyz> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Addresses the
mail listlimitations from the issue, ininternal/imap(
client.go,types.go) andinternal/cli(mail.go,cli.go):--unreadnow issues IMAPSEARCH UNSEENand anew
--flaggedissuesSEARCH FLAGGED. Previously--unreadfetched Nmessages and filtered client-side, so you got back fewer than the limit; the
count now respects
--limit.MessageSummarygainsfrom_address,to,message_id, andin_reply_to. All four come straight from theENVELOPEalready fetched, so there is zero extra fetch cost.
from_addresslets youmatch on sender/domain without a per-message
mail read;in_reply_toletsyou reconstruct reply chains from list output.
--fields uid,from_address,subjectprojects JSON outputto the requested keys, and
--compactemits a bare JSON array instead of thewrapper object. Both affect
--jsonmode only; text output is unchanged.ListMessageswas refactored to take aListOptionsstruct instead ofpositional parameters (as suggested in the issue), and all call sites (drafts,
watch) were updated. README documents the new flags, fields, and JSON shape.
Tests
Added
internal/imap/list_options_test.go(real in-memory IMAP server, no newdependency) and
internal/cli/mail_list_fields_test.go, covering all sevencategories:
--fieldsrejects unknown/typo'd field names instead of silently ignoringthem.
projection scales linearly over 5k messages.
and deterministic.
paginateSeqNumspagination math;splitFieldList; projectionkeeps only requested keys.
SEARCH UNSEENstill returns the unread ones (the exact client-side-truncation bug); flagged
filtering; projected output round-trips through JSON.
from_address/to/message_id/in_reply_to) populated from the ENVELOPE; a requested-but-empty field isemitted as null for a stable shape.
encoding covered by the tests above); explicit skip placeholders retained.
go build ./... && go vet ./... && go test ./...all green.Closes #9
🤖 Generated with Claude Code