Skip to content

Releases

mac 0.7.2

Choose a tag to compare

@31Carlton7 31Carlton7 released this 28 Oct 22:20

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, unread and search used 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, forward and every write used to scan all of Mail's message headers to find one id.
  • mailboxes reads 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-cache now works on every mail command that reads the account list.

mac 0.7.1

Choose a tag to compare

@31Carlton7 31Carlton7 released this 28 Oct 21:17

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

Choose a tag to compare

@31Carlton7 31Carlton7 released this 15 Oct 19:39

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

  • list the newest messages of any mailbox, and mailboxes with unread and total counts.
  • mark-unread, flag, unflag, move --to, copy --to and delete, next to the existing mark-read and archive. All of them act on exact ids, take several ids at once, and take --dry-run.
  • label add for Gmail labels, and junk / not-junk.
  • mailboxes create, rename and delete.
  • reply [--all] and forward --to. Both leave a draft open unless you pass --send.
  • draft and send take --html, --markdown, their -file forms, --body-file and repeatable --attach.
  • wait blocks until a matching message arrives (great for verification codes), and watch streams new matches as JSON lines.
  • extract pulls verification codes, links and unsubscribe targets out of a message, and unsubscribe acts on them.
  • senders ranks who sends you the most mail, and contacts finds addresses you've exchanged mail with.
  • open shows 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

  • --scan is ignored when the index is readable, since there is no window anymore. It prints a note.
  • unread without --account is now the newest unread mail across every account, not a sample.
  • Plain words in search must all match now. 0.6 matched the whole query as one piece of subject or sender.
  • mark-read and archive print a per-id receipt, {"results":[...]}, instead of {"markedRead":...} / {"archived":...}. Every mail mutation exits 1 if 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 --mailbox or --account.
  • Erasing. delete moves to Trash, and refuses a message already in Trash.
  • Unsubscribing by accident. unsubscribe is 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 last index 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 --attach has 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.
  • extract finds 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.