zele
zele is a multi-account email and calendar CLI for Gmail, IMAP/SMTP (Fastmail, Outlook, any provider), and Google Calendar. It reads, searches, sends, replies, forwards, archives, stars, and trashes e
SKILL.mdsynced-skills/zele/SKILL.md
GitHubzele
Every time you use zele, you MUST fetch the latest README:
curl -s https://raw.githubusercontent.com/remorses/zele/main/README.md # NEVER pipe to head/tail, read the full output
Then run the CLI help once — it already includes every subcommand, option, and flag:
zele --help # NEVER pipe to head/tail, read the full output
The README and zele --help output are the source of truth for commands, options, flags, the Google vs IMAP feature matrix, search operators, and the headless login flow.
Rules
- Never use the TUI. Running
zelewith no subcommand launches a human-facing TUI. Agents must use the CLI subcommands (zele mail list,zele cal events, etc.) which output structured YAML. - Always run
zele whoamifirst when the user asks to operate on a specific account. Pick the exact email from the output and pass it with--account. Never guess account emails. - Never truncate
--helpor README output withhead,tail,sed,awk, orless. Critical rules are spread throughout. Read them in full. - Parse YAML output with
yq, not regex. Pipe IDs throughxargsfor bulk actions. Always use--limit 100(or higher) so you don't miss threads:# read all unread emails zele mail list --filter "is:unread" --limit 100 | yq '.[].id' | xargs zele mail read # bulk archive zele mail list --filter "is:unread" --limit 100 | yq '.[].id' | xargs zele mail archive - Google-only features (labels, Gmail filters,
zele cal *, full profile) fail on IMAP accounts with a clear error. Checkzele whoamioutput for account type before using them. - Headless Google login requires a tmux wrapper because
zele loginis interactive. See the README "Remote / headless login" section for the exact pattern. - After sending, wait for the reply with
zele mail watch. Do not end your turn and do notsleep.mail sendandmail replyprint the exact command to run next, like# Wait for the reply: zele mail watch --account ... --filter '...' --timeout 259200. Run it right away. It blocks until the first new email matching the filter arrives (only emails that arrive after it starts can match), prints it with anelapsedfield, and exits:zele mail send --to bob@example.com --subject "Question" --body "Hey, can you check this?" zele mail watch --account me@example.com --filter 'from:bob@example.com subject:"Question"' --timeout 259200 # match: exit 0, then read it before answering zele mail read <thread_id from the watch output>- Exit 0 means a match. Exit 1 means timeout (
Timed out after 3d 0h 0m 0s ...). On timeout, run watch again or tell the user nobody replied yet. --timeout 259200is 3 days. People can take days to answer. Disable your shell tool timeout (e.g.timeout: 0) or set it longer than--timeout, or the tool kills the wait first. Every minute watch prints# Still watching, 3m 0s elapsedto stderr.- Network errors do not stop the watch. It prints
# Poll failed ..., retryingand polls again. Only auth errors stop it. - If the matched email is not the expected one, run watch again with a more specific filter.
- Also use it for verification codes or any expected email:
zele mail watch --filter 'subject:verification' --timeout 300.
- Exit 0 means a match. Exit 1 means timeout (
- Check reply recipients before sending with
zele mail reply <thread-id> --dry-run. Recipients are inferred from the thread, not from the sender of the last message, so a thread whose last message you sent still replies to the other person. If a reply would only reach the account's own address, zele refuses to send:
Never work around a# see to / cc / subject / In-Reply-To without sending zele mail reply <thread-id> --dry-run # override the inferred recipient zele mail reply <thread-id> --to paul@acme.com --body "..." # deliberately reply to yourself (normally refused) zele mail reply <thread-id> --allow-self --body "..."SelfRecipientErrorby switching tozele mail send; pass--totozele mail replyinstead, so threading headers stay correct. - Read a thread before replying.
zele mail replyandzele mail send --thread-idfail withUnseenLatestErrorunlesszele mail read <thread-id>already showed the live last message.mail watchandmail listdo not count. If a new reply arrives after you read, read again before sending:
Never passzele mail read <thread-id> zele mail reply <thread-id> --body "..."--forceto skip this unless the user explicitly asks to send without reading. - Send into an existing thread with
zele mail send --thread-id <thread-id>when you need full control of recipients and subject but still want correctIn-Reply-To/Referencesheaders. Recipients and subject are inferred from the thread when omitted. The same read-before-reply rule applies. - Format email bodies like a human wrote them. These rules apply to
--bodyand to every draft you show the user for approval:- Never hard-wrap. Write each paragraph as one line. Separate paragraphs with one blank line. Do not break lines at 72-80 columns: mail clients wrap text themselves, and hard breaks render as lines broken mid-sentence. Show drafts exactly as they will be sent.
- Capitalize the first word of every paragraph, also after a greeting that ends with a comma. Write
Buongiorno,thenSono Tommaso..., notsono Tommaso.... Same in English:Hi Bob,thenCan we....
Buongiorno, Sono Tommaso De Rossi e vorrei prenotare una visita presso la vostra sede di Genova. Potreste indicarmi le prime date disponibili? Grazie mille! Tommaso De Rossi - Free
@zele.shaddresses (receive only for now). These are permanent, real addresses, not throwaway ones: use them when the user wants a new address for themselves, a project, or an agent. Mail stays until the user deletes it. Each owner (a @gmail.com address, no other domains) gets up to 10 inboxes. They work like any account with--account name@zele.shfor list, read, search, watch, star, archive, trash and attachments. Sending, drafts and labels fail with a receive-only error.# 1. pick the owner: a @gmail.com account from `zele whoami` (type: google). # The sign-in code is then read from that Gmail automatically. zele login zele --owner you@gmail.com --name tommy # sign in + create tommy@zele.sh zele inbox create bills # more inboxes, already signed in zele inbox list # 2. wait for a signup / verification email zele mail watch --account bills@zele.sh --filter 'subject:verify' --timeout 600- Without
--ownerin an agent shell,login zelefails and lists the Gmail accounts in zele. Pick one, do not guess. - If the owner Gmail is not a zele account, the code is emailed and the command prints
Rerun with: zele login zele --owner ... --code <code>. Ask the user for the code. zele inbox delete <address> --forcedeletes the inbox and all its mail. The address can never be created again, so confirm with the user first.zele logout zele --forceremoves the session and the inbox accounts from this machine. Inboxes and mail stay on the server.
- Without