A terminal-based email client with Vim-style modal keybindings, written in Go.
vimail brings the speed of Vim navigation to your inbox with a 3-pane layout, modal editing, and multiple color themes.
- Vim-style modal editing — Normal, Insert, Visual, and Command modes
- 3-pane layout — Mailbox sidebar, message list, and preview pane
- 10 color themes — vimail, tokyonight, catppuccin, kanagawa, gruvbox, nord, matrix, cliamp, omarchy, system
- Hot-swappable themes — Switch with
:theme <name>at any time - AI compose assistant —
:aiin the editor to draft or rewrite emails using any CLI agent - Compose with Vim — Full Vim keybindings in the message body editor: operators with motions (
dw,cw,d$), count prefixes (2dd,3x,3J), linewise visual delete (V+d), and/?nNsearch - MCP server —
vimail mcplets local AI clients (Claude Code, Claude Desktop) read, search, review, and manage your mail through the offline cache and queue — sending stays off unless you opt in - Global search — Press
/to search across all accounts and folders - Multiple accounts — Manage several email accounts in one view
- Export to ZIP — Press
Eto export messages with text, HTML, metadata, and attachments - Attachments — View metadata in preview, save to disk with
S - Visual mode batch ops — Select messages with
v, then delete (d) or mark as read (r) - HTML email rendering — Clean text conversion via html2text, open raw HTML in browser with
o - JSON auto-format — Pretty-prints JSON bodies in the preview pane
- Incremental sync — Per-account IMAP sync with loading indicators
- Offline operation queue — Deletes, sends, and mark-read ops are queued in SQLite and retried on reconnect with exponential backoff; safe to share between the TUI and the MCP server (each op runs exactly once)
- Pure Go — No CGO required, single static binary
- Go 1.26+ (only when building from source)
- A terminal with truecolor support (
COLORTERM=truecolor)
Grab the latest release from GitHub Releases — no Go required.
git clone https://github.com/gausejakub/vimail.git
cd vimail
go build -o vimail .Move the binary somewhere on your $PATH:
mv vimail ~/.local/bin/go install github.com/gausejakub/vimail@latestvimailIf built locally without moving to $PATH:
./vimailvimail launches in fullscreen (alt-screen) mode. Press q or :quit to exit.
Subcommands:
| Command | Purpose |
|---|---|
vimail |
Launch the TUI |
vimail setup |
Store account credentials in the OS keyring |
vimail mcp |
Run the MCP server on stdio for AI clients (see MCP server) |
vimail help |
Show usage |
- Add accounts to
~/.config/vimail/config.toml(see examples below) - Run
vimail setupto store credentials in your OS keyring
vimail setupThis walks through each configured account and securely stores your password in the OS keyring. If credentials already exist, it will ask before overwriting.
vimail reads its config from ~/.config/vimail/config.toml. If the file doesn't exist, defaults are used.
[general]
preview_pane = true
[theme]
name = "tokyonight"Gmail requires an app password (regular passwords don't work with IMAP):
- Enable 2-Factor Authentication at https://myaccount.google.com/security
- Generate an app password at https://myaccount.google.com/apppasswords
- Copy the 16-character code
- Add to config:
[[accounts]]
name = "Gmail"
email = "you@gmail.com"
imap_host = "imap.gmail.com"
imap_port = 993
smtp_host = "smtp.gmail.com"
smtp_port = 587
auth_method = "app-password"
tls = "tls"- Run
vimail setupand paste the 16-character app password when prompted
Most providers work with a regular password or app password:
[[accounts]]
name = "Personal"
email = "alice@example.com"
imap_host = "imap.example.com"
imap_port = 993
smtp_host = "smtp.example.com"
smtp_port = 587
auth_method = "plain" # "plain" | "app-password"
tls = "tls" # "tls" (default) | "starttls" | "none"Common provider settings:
| Provider | IMAP Host | IMAP Port | SMTP Host | SMTP Port |
|---|---|---|---|---|
| Gmail | imap.gmail.com | 993 | smtp.gmail.com | 587 |
| Outlook/Hotmail | outlook.office365.com | 993 | smtp.office365.com | 587 |
| Yahoo | imap.mail.yahoo.com | 993 | smtp.mail.yahoo.com | 587 |
| iCloud | imap.mail.me.com | 993 | smtp.mail.me.com | 587 |
| Seznam.cz | imap.seznam.cz | 993 | smtp.seznam.cz | 465 |
After adding accounts, run vimail setup to store credentials.
| Key | Action |
|---|---|
j / k |
Move down / up in current pane |
10j / 5k |
Move N lines down / up |
h / l |
Switch pane left / right |
gg / G |
Jump to top / bottom |
500gg / 500G |
Jump to line N |
Ctrl+D / Ctrl+U |
Half-page scroll (preview) |
Tab / Shift+Tab |
Next / previous pane |
| Key | Action |
|---|---|
c |
Compose new message |
r |
Reply to selected message |
f |
Forward |
dd |
Delete message |
S |
Save attachments to ~/Downloads |
E |
Export message(s) to ZIP |
o |
Open in browser |
R |
Refresh |
Enter |
Open draft (in Drafts folder) |
Ctrl+S |
Send message (in compose) |
Esc |
Close overlay / save draft / clear search |
/ |
Search all accounts and folders |
| Key | Action |
|---|---|
: |
Enter command mode |
v / V |
Enter visual mode (select range, d to delete, r to mark read) |
? |
Toggle help overlay |
q |
Quit |
| Command | Action |
|---|---|
:quit / :q |
Quit vimail |
:theme <name> |
Switch theme |
:sync |
Sync mail |
:ai |
AI-assisted compose (default agent) |
:ai <name> |
AI-assisted compose with a specific agent |
:ops / :queue |
Show operation queue log |
:ps / :processes |
Show running background processes |
:search <query> / :s <query> |
Search messages across all accounts |
vimail tokyonight catppuccin kanagawa gruvbox nord matrix cliamp omarchy system
cliamp— retro Winamp-style palette: black background, hot-green accent, VU-meter status colors.omarchy— adapts to the Omarchy ecosystem. When Omarchy's active theme palette (~/.local/state/omarchy/current/theme/colors.toml) exists, vimail uses its exact semantic colors (accent, selection, muted, background shades); otherwise it falls back to the terminal's ANSI-16 palette, which Omarchy repaints on every theme switch anyway. After switching Omarchy themes, run:theme omarchyto re-read the palette.system— plain terminal ANSI-16 colors; also matches any terminal-theming setup.
vimail can use any CLI-based AI tool to help draft, rewrite, or reply to emails. Type :ai in the compose editor and the current body is sent to the AI agent — the response replaces the editor content.
- Open compose (
c), reply (r), or a draft (Enter) - Type a prompt in the body (e.g. "write a polite decline to this meeting")
- Press
Escto enter normal mode - Type
:aiand pressEnter - The hint line shows "Thinking..." while the agent runs
- The response replaces the editor body — review, edit, and send with
Ctrl+S
When replying, the quoted text (lines starting with >) is included as context, so the agent writes a reply to the original message.
Out of the box, vimail uses Claude Code CLI. If claude is in your $PATH, no configuration is needed — just use :ai.
Add an [ai] section to ~/.config/vimail/config.toml to define one or more agents:
[ai]
default = "claude"
[[ai.agents]]
name = "claude"
cmd = "claude"
args = ["--print", "-p", "{prompt}"]
[[ai.agents]]
name = "ollama"
cmd = "ollama"
args = ["run", "llama3.2", "{prompt}"]
[[ai.agents]]
name = "gemini"
cmd = "gemini"
args = ["-p", "{prompt}"]
[[ai.agents]]
name = "gpt"
cmd = "sgpt"
args = ["--no-md", "{prompt}"]
[[ai.agents]]
name = "local"
cmd = "llm"
args = ["-m", "mistral", "{prompt}"]Each agent needs:
| Field | Description |
|---|---|
name |
Identifier used with :ai <name> |
cmd |
Binary name or path (must be in $PATH) |
args |
Arguments passed to the binary. {prompt} is replaced with the full prompt |
The {prompt} placeholder is replaced with the system prompt (which includes To, Subject, and compose context) plus the editor body.
| Command | What happens |
|---|---|
:ai |
Uses the default agent |
:ai ollama |
Uses the agent named "ollama" |
:ai gpt |
Uses the agent named "gpt" |
Any tool that accepts a prompt as an argument and prints the response to stdout will work:
- Claude Code CLI —
claude --print -p - Ollama —
ollama run <model> - llm —
llm -m <model> - Gemini CLI —
gemini -p - sgpt —
sgpt --no-md - aichat —
aichat -m <model> - mods —
mods
vimail mcp runs a Model Context Protocol server on stdio, so local AI clients (Claude Code, Claude Desktop) can work with your email through vimail's cache and offline queue. Point your client at the binary:
{
"mcpServers": {
"vmail": { "command": "vimail", "args": ["mcp"] }
}
}Reads (list_accounts, list_folders, list_messages, read_message, search_messages) are served from the local cache; search results expose usable folder/UID handles and a truncated flag. For natural review tasks, list_recent_messages can sync and list a time window across every account in one call, excluding non-received folders and collapsing Gmail label copies; read_messages then opens the selected bodies as one batch and can fetch missing bodies without marking messages read.
Draft tools are local-only. Server writes (mark_read, mark_all_read, delete_message, restore_messages) use the durable operation queue. mark_read, delete, and restore accept either one uid or a batch in uids, producing one queue row per batch; mark_all_read creates an authoritative whole-folder operation for every selected server mailbox, including Spam and Trash by default, so it covers uncached messages without UID discovery. Deletion only ever moves to Trash, so permanent deletion stays in the TUI. Restore is server-first: Trash remains unchanged in cache until delivery succeeds, then destination UIDs are reconciled without relying on incremental sync. list_operations reports delivery, retry, and failure state. The sync tool refreshes an account or folder on demand and delivers queued writes; full: true rebuilds cached headers authoritatively when server-side moves or deletes made the incremental cache stale.
The MCP server is safe to run alongside the TUI: queued operations are claimed exactly once, account syncs are serialized across processes, and the TUI refreshes its folder counts and message list when the MCP process changes the cache. A standalone MCP process connects to IMAP lazily on the first write and retries queued operations every minute; outcomes are visible through MCP's list_operations and the TUI's :ops view.
A request such as “review yesterday's mail and tell me what matters” normally takes two MCP calls: list_recent_messages with fresh=true, followed by one read_messages batch with fetch_missing=true. Importance remains the AI client's judgment rather than a hard-coded sender or subject filter.
Sending email is disabled by default. Any connected MCP client can act as you, so outbound mail requires an explicit opt-in in ~/.config/vimail/config.toml:
[mcp]
allow_send = false # set to true to expose the send_email toolWith allow_send = true, send_email sends via the account's SMTP server and archives to Sent. When it is false (or the section is absent), the tool is not registered at all — clients never see it.
The MCP process logs to ~/.local/share/vimail/vimail-mcp.log.
vimail writes structured JSON logs to ~/.local/share/vimail/vimail.log. Every background operation (sync, fetch, send, delete, mark-read), user action, and error is logged with full context (account, folder, UID, duration).
The active log rotates when it reaches 10 MB, keeping one previous file (vimail.log.1). Retention is enforced both at startup and hourly while vimail runs: the active log is rotated once its oldest entry is more than 3 days old, and a log file is deleted once all of its entries are more than 3 days old.
# Tail logs in real time
tail -f ~/.local/share/vimail/vimail.log | jq .
# Filter errors
cat ~/.local/share/vimail/vimail.log | jq 'select(.level == "error")'
# Show sync operations for a specific account
cat ~/.local/share/vimail/vimail.log | jq 'select(.op == "sync" and .account == "you@gmail.com")'main.go Entry point, subcommands (setup, mcp, help)
internal/
config/ TOML config loading
auth/ OS keyring, OAuth2 device flow, setup CLI
email/ Domain types (Account, Folder, Message), Store interface
ai/ AI agent CLI wrapper (claude, ollama, etc.)
logging/ Async structured JSON logger with rotation
cache/ SQLite schema + Store implementation, multi-process op queue
worker/ IMAP worker, SMTP worker, Coordinator
mcp/ MCP server (stdio) and its tools
mock/ Mock data for dev mode
theme/ Theme engine + 10 themes
tui/
app.go Root bubbletea model
keys/ Mode enum + keybinding maps
util/ Shared cross-component message types
layout/ Container, split pane, overlay
components/
mailbox/ Account & folder sidebar
msglist/ Message list with viewport + visual mode
preview/ Message preview with scroll + browser open
compose/ Compose overlay with Vim editor
help/ Help overlay
status/ Status bar (mode badge, info)
pkg/
vimtea/ Vim-style editor widget
go test ./...
(cd pkg/vimtea && go test ./...) # the editor is a separate module; CI runs bothvimail is functional with real IMAP/SMTP connectivity, app-password auth, SQLite message caching, incremental sync, and an offline operation queue. Falls back to mock data when no accounts are configured.
MIT