Repository navigation
Releases
mac 0.7.2
Mail is faster across the board. Every output is byte-identical to 0.7.1.
| Command | 0.7.1 | 0.7.2 |
|---|---|---|
mac mail list |
257 ms | 19 ms |
mac mail unread |
66 ms | 18 ms |
mac mail read <id> |
41 ms | 19 ms |
mac mail mailboxes |
44 ms | 18 ms |
mac mail mark-read <id> (dry run) |
208 ms | 17 ms |
mac mail search from:github.com |
40 ms | 20 ms |
- Listing ranks only the newest matches. Mail stores some messages more than once.
list,unreadandsearchused to rank every matching message to collapse those copies, then kept 20. Now they read the newest matches first and rank only those, whenever that can't change the answer. - Finding a message by id uses Mail's own index.
read,extract,reply,forwardand every write used to scan all of Mail's message headers to find one id. mailboxesreads the unread counts Mail already keeps.- Commands that change mail use the account cache too, so they no longer ask Mail.app for its account list on every run. Each write still targets an account by its id from Mail's index.
--no-cachenow works on every mail command that reads the account list.
mac 0.7.1
Mail reads no longer stall when macOS touches its account database.
0.7.0 cached Mail's account list and kept it only while the whole system account store (~/Library/Accounts/Accounts4.sqlite) was unchanged. macOS rewrites that store on routine token refreshes and every time Mail starts, so the cache was thrown away often. The next read then asked Mail.app for its accounts, launching Mail if it wasn't running, and one mac mail unread took 21 seconds.
The cache now only cares about what decides Mail's answer: each account's id, name, parent, type and user name, and whether Mail is switched on for it. Renaming, adding or removing an account, or switching Mail off for one, still refreshes it.
The first mail command after upgrading rebuilds the cache once.
mac 0.7.0
This one is all Mail. mac mail used to be the slowest thing in the tool, and it could only see the newest 30 messages of each inbox. In 0.7 it reads Mail's own database and message files directly, so it sees every message in every account, it doesn't need Mail.app to answer a read, and most commands finish in tens of milliseconds.
You need Full Disk Access now
Mail reads come from Mail's Envelope Index and the message files under ~/Library/Mail, and macOS only lets your terminal read those with Full Disk Access. Grant it in System Settings › Privacy & Security › Full Disk Access, then run mac doctor. It has a new mailEnvelopeIndex row that tells you whether Mail reads will work.
Without it, unread, search, read, mark-read, archive and a plain inbox list still work the old way, through Mail.app, with a warning: only the newest messages of each inbox, and plain words only. Everything new in this release needs the index and exits 2 without it. draft, send, open, accounts and mailboxes create/rename/delete don't care.
Faster
Same Mac, same commands, 0.6.0 against 0.7.0:
| Command | 0.6.0 | 0.7.0 |
|---|---|---|
mac mail unread |
2.5 s | 0.07 s |
mac mail unread --account <Exchange> |
4.9 s | 0.03 s |
mac mail search invoice |
44.7 s, and it found nothing | 0.07 s, 20 results |
mac mail read <id> |
6.2 to 13.5 s | 0.06 s |
Mail's account list is cached against the system account store, so a read makes no Mail.app call at all once it's warm. An account name the cache doesn't know is checked with Mail.app before mac says it doesn't exist.
Search that actually searches
mac mail search now covers every mailbox and takes operators: from:, to:, cc:, subject:, has:attachment, is:unread|read|flagged|priority, in:, account:, category:, and after:/before:/newer:/older: with dates like 2026-10-01, yesterday or 3d. Put - in front of any term to exclude it, and quote phrases.
mac mail search 'from:github.com is:unread newer:2d'
mac mail search 'has:attachment -category:promotions newer:30d'
Body search is new too. Run mac mail index build once (about 80 seconds for 107k messages on my Mac) and body: works everywhere the operators do:
mac mail search 'body:"tracking number" newer:30d'
The index lives in ~/Library/Caches/mac-cli/mail-bodies.sqlite. It holds the words of your mail, so it's private to your user, and mac mail index clear deletes it. Nothing updates it in the background; run index build again to catch up, and search tells you when it's behind.
New commands
listthe newest messages of any mailbox, andmailboxeswith unread and total counts.mark-unread,flag,unflag,move --to,copy --toanddelete, next to the existingmark-readandarchive. All of them act on exact ids, take several ids at once, and take--dry-run.label addfor Gmail labels, andjunk/not-junk.mailboxes create,renameanddelete.reply [--all]andforward --to. Both leave a draft open unless you pass--send.draftandsendtake--html,--markdown, their-fileforms,--body-fileand repeatable--attach.waitblocks until a matching message arrives (great for verification codes), andwatchstreams new matches as JSON lines.extractpulls verification codes, links and unsubscribe targets out of a message, andunsubscribeacts on them.sendersranks who sends you the most mail, andcontactsfinds addresses you've exchanged mail with.openshows a message in Mail.app.
Items also carry more of what Mail knows: mailbox, to, cc, isFlagged, hasAttachments, isPriority, Mail's on-device category, and its Apple Intelligence summary when it made one. read adds an attachments list. Every 0.6 key is still there.
Changed
--scanis ignored when the index is readable, since there is no window anymore. It prints a note.unreadwithout--accountis now the newest unread mail across every account, not a sample.- Plain words in
searchmust all match now. 0.6 matched the whole query as one piece of subject or sender. mark-readandarchiveprint a per-id receipt,{"results":[...]}, instead of{"markedRead":...}/{"archived":...}. Every mail mutation exits1if any one of its ids wasn't changed, so read the results.
Things mac refuses to do
- Gmail archive and Gmail move. A scripted move can leave the label on the server. Archive Gmail in Mail.
label remove. Mail's scripting has no label command. Remove labels in Mail.- Guessing. A message with copies in several mailboxes is refused until you pick one with
--mailboxor--account. - Erasing.
deletemoves to Trash, and refuses a message already in Trash. - Unsubscribing by accident.
unsubscribeis a dry run unless you pass--yes, and it never opens a web link for you.
Known limitations
- Search folds case for ASCII letters only, and there's no
OR. body:matches whole words (body:invo*for a prefix), sees mail up to the lastindex build, and skips the handful of messages whose text Mail keeps outside the message file.- HTML set by script comes out of Mail with its plain-text alternative quoted (
>on every line). - A reply or forward with a body or
--attachhas its quote rebuilt by mac from the original's text, so the original's formatting and inline images are lost. - There's no
--from. Mail picks the sending account. extractfinds codes by heuristics, and it can miss codes in images or split by spaces.- Trash, Junk, Sent and system mailboxes are recognised by name. Unusual or localized names may need the exact path.