Skip to content

Repository files navigation

vimail

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.

Features

  • 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:ai in 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 / ? n N search
  • MCP servervimail mcp lets 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 E to 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

Requirements

  • Go 1.26+ (only when building from source)
  • A terminal with truecolor support (COLORTERM=truecolor)

Install

Download binary

Grab the latest release from GitHub Releases — no Go required.

From source

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

go install github.com/gausejakub/vimail@latest

Usage

vimail

If built locally without moving to $PATH:

./vimail

vimail 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

Account Setup

  1. Add accounts to ~/.config/vimail/config.toml (see examples below)
  2. Run vimail setup to store credentials in your OS keyring
vimail setup

This walks through each configured account and securely stores your password in the OS keyring. If credentials already exist, it will ask before overwriting.

Configuration

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"

Adding a Gmail account

Gmail requires an app password (regular passwords don't work with IMAP):

  1. Enable 2-Factor Authentication at https://myaccount.google.com/security
  2. Generate an app password at https://myaccount.google.com/apppasswords
  3. Copy the 16-character code
  4. 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"
  1. Run vimail setup and paste the 16-character app password when prompted

Adding other providers

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.

Keybindings

Navigation

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

Actions

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

Modes

Key Action
: Enter command mode
v / V Enter visual mode (select range, d to delete, r to mark read)
? Toggle help overlay
q Quit

Commands

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

Available themes

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 omarchy to re-read the palette.
  • system — plain terminal ANSI-16 colors; also matches any terminal-theming setup.

AI Compose Assistant

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.

How it works

  1. Open compose (c), reply (r), or a draft (Enter)
  2. Type a prompt in the body (e.g. "write a polite decline to this meeting")
  3. Press Esc to enter normal mode
  4. Type :ai and press Enter
  5. The hint line shows "Thinking..." while the agent runs
  6. 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.

Default setup

Out of the box, vimail uses Claude Code CLI. If claude is in your $PATH, no configuration is needed — just use :ai.

Configuring agents

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.

Usage examples

Command What happens
:ai Uses the default agent
:ai ollama Uses the agent named "ollama"
:ai gpt Uses the agent named "gpt"

Compatible CLI tools

Any tool that accepts a prompt as an argument and prints the response to stdout will work:

MCP server (AI clients)

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 tool

With 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.

Logs

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")'

Project structure

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

Running tests

go test ./...
(cd pkg/vimtea && go test ./...)   # the editor is a separate module; CI runs both

Status

vimail 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.

License

MIT

About

Terminal email client with Vim keybindings

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages