Mutt-wizard post-install configuration

Published on

A few configuration steps after installing mutt-wizard. Includes a couple of patches and general configuration changes.

Background #

This part speaks about my motivation to go through this change, feel free to skip it if you are interested only in the configuration details.

As part of my 2021 year resolution, I decided to clean up my existing Gmail accounts and move as much stuff as possible to my own domain. The overall migration plan is:

  1. Extract passwords from Chrome and import into a proper password manager
  2. Set up mail accounts on my Linux machine
  3. Go through the mounds of mail:
    • Remove accounts which are no longer needed and I forgot they existed
    • Unsubscribe from subscriptions I no longer read
    • Create new aliases for accounts that I want to keep and configure accounts on those services
    • Issue CCPA requests to remove my data from the services that comply with CCPA
    • Delete the mail that I absolutely do not need
    • Save the mail that I might need later

The first step was trivial, Chrome allows exporting passwords as csv. Password-store allows creating arbitrary files, so that csv is being hacked away one account at a time.

For second step, given my love for vim-like UIs, I chose neomutt. Out of the box, it takes a few steps to configure an account in neomutt, but there is a script called ‘mutt-wizard’ that takes care of the basic configuration, setting up:

Config changes #

My configuration:

Neomutt screenshot

Config files at play #

  1. User mutt config — the user-level neomutt config, typically ${HOME}/.config/mutt/muttrc
  2. Account mutt config — the configuration file corresponding to the individual mail account. Generated by mutt-wizard. Typically placed in ${HOME}/.config/mutt/accounts

The way account switching is configured is mutt-wizard binds a macro i<ACCOUNT_NUMBER>, e.g. i1 to source the first account config and switch to it.

Date format and tags #

Neomutt provides an option to set conditional date formatting for index (aka the mail list view). To set the format to display the following:

# Test  Date Range  Format String  Example
# --------------------------------------------
# %[d   Today       [%H:%M    ]      12:34
# %[m   This month  [%a %d    ]      Thu 12
# %[y   This year   [%d %b    ]      10 Dec
#  —    Older       [%d/%m/%y ]      11/06/15

Add the option to user mutt config:

set index_format='%4C %Z %<[y?%<[m?%<[d?%[%H:%M   ]&%[%a %d  ]>&%[%d %b  ]>&%[%d/%m/%y]> %-15.15L (%?l?%4l&%4c?) %s

Above the account config references (macro index,pager i1...).

To display tags after the pipe symbol, append | %g to index_format string.

Virtual mailboxes and separator #

To clean up a lot of mail I decided to go with a basic system:

Mutt-wizard configures neomutt to use Maildirs for physical representation of email on the hard drive, creating the directories corresponding to the ones in Gmail. To allow for tagging, it is necessary to add virtual mailboxes. Virtual mailboxes here represent a result of notmuch query.

To set up virtual mailboxes

  1. Specify nm_default-url in user’s config to the value of where mutt-wizard configured account directories:

    set nm_default_url = "notmuch://${HOME}/.local/share/mail/"
  2. Add the virtual mailboxes to the account config:

    virtual-mailboxes "Inbox" "notmuch://?query=folder:/$from/ and -folder:\"/Trash/\" and -tag:todo and -folder:\"/Saved/\""
    virtual-mailboxes "Todo" "notmuch://?query=tag:todo and folder:/$from/"

    This configuration implements the following:

    • Email is located in account-specific folder. This allows to display all emails sent to “”.
    • Email is not in Trash — in the cleanup workflow, unneeded email is moved to Trash.
    • Tag ’todo’ is excluded from “Inbox”. Tagged emails are to be followed-up later.
    • ‘Saved’ maildir is excluded from Inbox.
  3. If you want to open the virtual mailbox by default when switching the account, change the i<ACCOUNT_NUMBER> macro in user’s config:

    -macro index,pager i2 '<sync-mailbox><enter-command>source ${HOME}/.config/mutt/accounts/<enter><change-folder>!<enter>;<check-stats>' "switch to" # mw-autogenerated
    +macro index,pager i2 '<sync-mailbox><enter-command>source ${HOME}/.config/mutt/accounts/<enter><sidebar-open><enter>;<check-stats>' "switch to"

    The difference is opening the entry from sidebar as opposed to changing folder.

  4. To tag the email as “todo”, add the following line in either of the configuration files:

    macro index tt "<modify-labels>!todo -unread -inbox<enter>" "Toggle todo"

As for emails that I need to save for later, I just save them (s) to “Saved” mailbox to be later fed into backup.

Separator #

I decided to have a line between virtual mailboxes and the physical Maildirs. It can be configured like so:

  1. Create the directory:

    ~ > cd ~/.local/share/<ACCOUNTNAME>
    ~ > mkdir -p "__________"/{cur,new,tmp}
  2. Add the directory to list of mailboxes:

    1. Open the account configuration file in an editor (by default it’s in ~/.config/mutt/accounts/)

    2. Prepend "=__________" to the list of mailboxes, so it looks like:

      mailboxes "=__________" "=INBOX" ...

The cur,new,tmp directories need to be created, inside that directory to mimic the Maildir format and not make neomutt freak out.

Patches #

Note: patches in this post are based on commit #ebb81bbcaa2f5ea694432396a4fcb0c000a1b88c

The first few patches force use of MBSYNCRC file from my .config directory. Another approach would be to override it through environment file. The last two deal with notifications, one adding a notification icon from Paper color scheme.


Adjust the location of mbsyncrc to be inside .config
diff --git a/bin/mw b/bin/mw
index 129cda2..2ff31eb 100755
--- a/bin/mw
+++ b/bin/mw
@@ -9,7 +9,7 @@ muttrc="${XDG_CONFIG_HOME:-$HOME/.config}/mutt/muttrc"
 alias mbsync='mbsync -c "$mbsyncrc"'


Adjust the location of mbsyncrc
diff --git a/bin/mailsync b/bin/mailsync
index ecac6af..3761084 100755
--- a/bin/mailsync
+++ b/bin/mailsync
@@ -35,7 +35,7 @@ export GPG_TTY=$TTY


-[ -n "$MBSYNCRC" ] && alias mbsync="mbsync -c $MBSYNCRC" || MBSYNCRC="$HOME/.mbsyncrc"
+[ -n "$MBSYNCRC" ] && alias mbsync="mbsync -c $MBSYNCRC" || MBSYNCRC="$HOME/.config/mbsyncrc"

 # Settings are different for MacOS (Darwin) systems.
 case "$(uname)" in


Forces the usage of $MBSYNCRC
diff --git a/bin/mailsync b/bin/mailsync
index 3761084..d626856 100755
--- a/bin/mailsync
+++ b/bin/mailsync
@@ -54,7 +54,7 @@ esac
 # Check account for new mail. Notify if there is new content.
 syncandnotify() {
     acc="$(echo "$account" | sed "s/.*\///")"
-    if [ -z "$opts" ]; then mbsync "$acc"; else mbsync "$opts" "$acc"; fi
+    if [ -z "$opts" ]; then mbsync -c "$MBSYNCRC" "$acc"; else mbsync "$opts" "$acc"; fi
     new=$(find "$HOME/.local/share/mail/$acc/INBOX/new/" "$HOME/.local/share/mail/$acc/Inbox/new/" "$HOME/.local/share/mail/$acc/inbox/new/" -type f -newer "${XDG_CONFIG_HOME:-$HOME/.config}/mutt/.mailsynclastrun" 2> /dev/null)
     newcount=$(echo "$new" | sed '/^\s*$/d' | wc -l)
     if [ "$newcount" -gt "0" ]; then


Fixes X binary lookup for Gentoo
diff --git a/bin/mailsync b/bin/mailsync
index 80140bf..db40b21 100755
--- a/bin/mailsync
+++ b/bin/mailsync
@@ -39,7 +39,7 @@ case "$(uname)" in
 		messageinfo() { osascript -e "display notification with title \"📧 $from\" subtitle \"$subject\"" ;}
-		displays="$(pgrep -a X\(org\|wayland\) | grep -wo "[0-9]*:[0-9]\+" | sort -u)"
+		displays="$(pgrep -a X\(org\|wayland\)\? | grep -wo "[0-9]*:[0-9]\+" | sort -u)"
 	    	notify() { for x in $displays; do
 				export DISPLAY=$x
 				notify-send --app-name="mutt-wizard" "mutt-wizard" "📬 $2 new mail(s) in \`$1\` account."


Adds an icon to the notification
diff --git a/bin/mailsync b/bin/mailsync
index db40b21..1241da7 100755
--- a/bin/mailsync
+++ b/bin/mailsync
@@ -42,7 +42,8 @@ case "$(uname)" in
 		displays="$(pgrep -a X\(org\|wayland\)\? | grep -wo "[0-9]*:[0-9]\+" | sort -u)"
 	    	notify() { for x in $displays; do
 				export DISPLAY=$x
-				notify-send --app-name="mutt-wizard" "mutt-wizard" "📬 $2 new mail(s) in \`$1\` account."
+				icon_name="/usr/share/icons/Paper/scalable/actions/mail-message-new-symbolic.svg"
+				notify-send --icon="$icon_name" --app-name="mutt-wizard" "mutt-wizard" "📬 $2 new mail(s) in \`$1\` account."
 			done ;}
 		messageinfo() { for x in $displays; do
 				export DISPLAY=$x