Skip to content

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.