carrier/crates
Kayos 4251f514e6 Phase B + folders: mail_folder_list, mail_search, mail_thread, mail_move
Phase B per the spec (multi-account already supported via the
account arg) + full folder support:

- mail_folder_list  : enumerate IMAP folders. Returns {name, delimiter,
                      attributes, selectable}. selectable=false flags
                      \Noselect mailboxes (parent-of-children only).
- mail_search       : raw IMAP SEARCH passthrough against any folder.
                      ALL/OR/NOT combinators supported. CR/LF in the
                      query rejected (anti-injection).
- mail_thread       : seed Message-ID -> matches the seed itself plus
                      any message whose References or In-Reply-To
                      contains the seed. Oldest-first ordering (root
                      -> leaves). Brackets on the seed are optional.
- mail_move         : UID MOVE (RFC 6851) with COPY + STORE +FLAGS
                      \Deleted + UID EXPUNGE fallback. Destination
                      folder must already exist.

Refactor: shared fetch_summaries() helper used by search + thread.

Smoke verified 2026-05-21 against kayos@sulkta.com:
- 6 folders listed (DMARC, Drafts, INBOX, Junk, Sent, Trash)
- SEARCH "SUBJECT \"mail-mcp smoke\"" finds uid 27
- THREAD on uid 27's Message-ID returns 1 msg (the seed)
- MOVE INBOX(27) -> Junk(2) -> INBOX(28) round trip clean

Build gotcha: async-imap's uid_store and uid_expunge return non-Unpin
streams (unlike uid_fetch). Pinned via Box::pin inside scoped blocks.
2026-05-21 07:26:44 -07:00
..
mail-mcp Phase B + folders: mail_folder_list, mail_search, mail_thread, mail_move 2026-05-21 07:26:44 -07:00