muttlike-imap
Search IMAP mailboxes from the command line using mutt-style patterns.
$ muttlike-imap "~f alice ~U" --summary
1 result(s):
UID:1264 | From:Alice <alice@example.com> | Date:Tue, 21 Apr 2026 15:02:35 +0000
Subject:Re: project update
Preview:Hi, here is the latest version of the document…
Why
Mutt has a great pattern language for finding messages (~f, ~s, ~d <7d,
!~U, (A | B) C, …). IMAP servers can do most of the same searches, but the
wire protocol is verbose and the bindings in imaplib are awkward to compose.
muttlike-imap is a small CLI that translates mutt patterns into IMAP SEARCH
criteria and prints the matches as JSON or a human-readable summary.
It's intentionally small and scriptable – useful as a building block for notification scripts, AI assistants, cron jobs, or one-off lookups.
Install
pip install muttlike-imap
Requires Python 3.9+. No third-party dependencies.
Shell completion
muttlike-imap ships completion scripts for zsh, bash, and fish. Add
one of the following to your shell rc file:
# zsh (~/.zshrc)
eval "$(muttlike-imap --completion zsh)"
# bash (~/.bashrc)
eval "$(muttlike-imap --completion bash)"
# fish (~/.config/fish/config.fish, or just save the output)
muttlike-imap --completion fish | source
The zsh script offers per-flag tailoring (file paths for --config,
(true false) for --imap-tls, env-var names for
--imap-password-env, live mailbox completion via
--list-mailboxes). Bash and fish provide flag-name completion plus
the same enumerations where applicable.
Configure
muttlike-imap looks for connection settings in this order (highest priority
first):
- CLI flags:
--imap-host,--imap-port,--imap-user,--imap-password-cmd,--imap-password-env,--imap-tls. - Environment variables:
IMAPQUERY_HOST,IMAPQUERY_PORT,IMAPQUERY_USER,IMAPQUERY_PASS,IMAPQUERY_TLS. - The file pointed to by
$IMAPQUERY_CONFIG, if set. $XDG_CONFIG_HOME/muttlike-imap/config(defaults to~/.config/muttlike-imap/config).~/.config/imap-smtp-email/.env: kept as a fallback for users who already have this file from the openclaw imap-smtp-email skill. Seedocs/openclaw.mdfor wiringmuttlike-imapinto an openclaw workspace.
A minimal config file:
IMAP_HOST=imap.example.com
IMAP_PORT=993
IMAP_USER=you@example.com
IMAP_PASS_CMD=pass email/imap.example.com
IMAP_TLS=true
The keys can be written with the IMAP_ prefix (shown above, matches mutt and
offlineimap conventions), with the IMAPQUERY_ prefix, or with no prefix at
all (HOST=…); pick whichever you prefer.
Passwords
There are three ways to give muttlike-imap your password, in increasing
order of how much they leak:
-
IMAP_PASS_CMD=<shell command>(recommended). The command is run on every invocation; the first line of stdout is used as the password. Pair withpass,secret-tool, macOS Keychain (security find-generic-password -w), or anything else that prints a secret to stdout. The password lives only in themuttlike-imapprocess memory and is never written to disk or to the environment.Equivalent CLI flag:
--imap-password-cmd "pass email/imap.example.com". -
--imap-password-env MY_VAR. Reads the password from a named environment variable. Useful when you already have a secret in a variable (e.g. viadirenvor a parent process) and don't want to re-export it. Caveat: env vars leak through/proc/<pid>/environ, child processes,ps eww, and crash dumps. -
IMAP_PASS=<plaintext>in the config file. Simplest, least secure; fine for throwaway accounts and local-only setups. Make sure the file ischmod 600.
See docs/secrets.md for worked examples with pass,
raw gpg, and OS keyrings.
Examples
By default the search runs against INBOX and returns the 10 most recent
matches as JSON. Pass --mailbox <name> to search elsewhere, --limit N
to widen or narrow the result count, and --summary for human-readable
output.
# Unread mail, default INBOX, summary view
muttlike-imap "~U" --summary
# All mail from Alice in the last week
muttlike-imap "~f alice ~d <7d" --summary
# Archived correspondence with Bob about a specific topic
muttlike-imap '~L bob ~s "project x"' --mailbox Archive --summary
# Anything addressed to me, last 30 days, that I haven't replied to
muttlike-imap '~p ~d <30d !~Q' --summary
# Date range, ISO format
muttlike-imap '~d 2025-09-01-2025-12-31 ~f committee' --summary
# JSON for piping into jq
muttlike-imap "~U" | jq '.[] | {uid, subject, from}'
# Discover available folders
muttlike-imap --list-mailboxes
Pattern syntax
A B is AND (juxtaposition), A | B is OR, !A is NOT, and (...) groups.
| Modifier | Meaning |
|---|---|
~f <text> |
From contains |
~t <text> |
To contains |
~s <text> |
Subject contains |
~b <text> |
Body contains |
~B <text> |
Body or any header contains |
~c <text> |
Cc contains |
~C <text> |
To, Cc, or Bcc contains |
~L <text> |
Any participant (From/To/Cc) |
~e <text> |
Sender header |
~i <text> |
Message-ID |
~y <text> |
X-Label |
~h "Name: text" |
Arbitrary header |
~x <text> |
References / In-Reply-To |
~A |
All messages |
~U / ~N |
Unread / new |
~R / ~O |
Read / old |
~F |
Flagged |
~D |
Deleted |
~Q |
Replied (answered) |
~p |
Addressed to you |
~P |
From you |
~d <Nu |
Date: header newer than N units (units y m w d H M S) |
~d >Nu |
Date: header older than N units |
~d =Nu |
Exactly N units old |
~d DATE |
On a specific date (YYYY-MM-DD or D/M/Y) |
~d -DATE / ~d DATE- |
Half-open range (before / since) |
~d DATE-DATE |
Range |
~d DATE*Nu |
Range of ±N units around DATE |
~r DATERANGE |
Same grammar but on received date (INTERNALDATE) |
~z <N / ~z >N / ~z N-M |
Size in bytes (suffix K/M) |
See docs/pattern-syntax.md for the full reference.
Limitations vs mutt
- No regex. IMAP
SEARCHis substring-only, so all text matches are literal. Anchors and character classes are not honored. - No mutt-runtime modifiers.
~T(tagged),~v(collapsed thread),~m(message-number),~n(score),~$,~#,~(...)(thread patterns), PGP modifiers: all error out instead of silently misbehaving.
H/M/S offsets are exact despite IMAP's day granularity: the server
narrows to the smallest whole-day window containing the precise range,
then a client-side post-filter trims the fetched candidates by their
Date: header (or INTERNALDATE for ~r). ~d <30M really does mean
"last 30 minutes". The post-filter is suppressed when the sub-day
modifier sits inside an OR, !, or paren-grouped disjunction, since
lifting its predicate to a top-level filter would change the pattern's
meaning.
Library use
from muttlike_imap.parser import parse_pattern
from muttlike_imap.client import search
from muttlike_imap.config import load_config
config = load_config() # or pass in your own dict
results = search(config, "~f alice ~U", limit=10, mailbox="INBOX")
for r in results:
print(r["subject"])
# Or just use the parser:
parse_pattern("(~f a | ~f b) ~U")
# → 'OR (FROM "a") (FROM "b") UNSEEN'
License
MIT: see LICENSE.
Release files for muttlike-imap 1.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| muttlike_imap-1.1.3.tar.gz | 45.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| muttlike_imap-1.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 71.2 kB
Release files / muttlike_imap-1.1.3.tar.gz
| Download URL | muttlike_imap-1.1.3.tar.gz |
|---|---|
| Size | 45.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
779d30bfdbbb6a7337a588b011e02da94f31550a35cf07bcfaee136902e0411e
|
|
BLAKE2b-256 checksum How to use checksums |
b95a9142a0478e4245f4f1569c41226eeb98f37a45eeb28b0e168505075ef1c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 27, 2026.
Transparency logRelease files / muttlike_imap-1.1.3-py3-none-any.whl
| Download URL | muttlike_imap-1.1.3-py3-none-any.whl |
|---|---|
| Size | 25.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d2d5c3a453b40b178f029b245c772724d5913b490441c8c83fa606c46f45d62a
|
|
BLAKE2b-256 checksum How to use checksums |
bf696503a559e7b799a35c53f877201ace7d653e3d75426b8fd1d559b14e552d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on May 27, 2026.
Transparency log