Skip to content

Releases: remorses/zele

zele@0.10.0

Choose a tag to compare

@remorses remorses released this 08 Oct 14:46
  1. Free, permanent @zele.sh email addresses (receive-only for now). Sign in with a @gmail.com owner and create up to 10 addresses for yourself, a project, or your agents:

    zele login zele --owner you@gmail.com --name tommy
    zele inbox create bills
    zele inbox list
    zele mail list --account tommy@zele.sh

    The sign-in code is read automatically from the owner Gmail when it is already a zele account; otherwise pass --code. Each inbox works with mail list, mail read, mail search, mail watch, star, archive, trash, spam, and attachments. Sending, drafts, and labels return a clear "receive-only" error. zele logout zele signs out and removes the inbox accounts from this machine.

  2. mail watch is now the way to wait for a reply after sending — mail send and mail reply print the exact watch command to run next:

    zele mail send --to bob@example.com --subject "Question" --body "Hey"
    # Wait for the reply: zele mail watch --account me@example.com --filter 'from:bob@example.com subject:"Question"' --timeout 259200
    • mail watch prints how long it waited: an elapsed field on the match, Timed out after 3d 0h 0m 0s on timeout, and # Still watching, 3m 0s elapsed on stderr every minute
    • mail watch no longer exits on a network error. It prints # Poll failed ..., retrying and polls again, so a watch can run for days. Auth errors still stop it
  3. Fixed mail watch on Gmail returning an old email as a match — it reused a history cursor from an earlier run and replayed emails that arrived since then. Now only emails that arrive after the command starts can match, same as IMAP.

zele@0.9.0

Choose a tag to compare

@remorses remorses released this 07 Oct 10:39
  1. Proton Mail Bridge and self-signed IMAP/SMTP servers — login imap now works with local TLS endpoints that use their own certificate:

    # Proton Bridge default (STARTTLS on 1143/1025), trusting the exported cert
    zele login imap \
      --email you@proton.me \
      --imap-host 127.0.0.1 --imap-port 1143 \
      --smtp-host 127.0.0.1 --smtp-port 1025 \
      --password "<bridge-password>" \
      --no-tls --ca ~/bridge/cert.pem
    
    # Bridge SSL mode: both listeners use implicit TLS
    zele login imap \
      --email you@proton.me \
      --imap-host 127.0.0.1 --imap-port 1143 \
      --smtp-host 127.0.0.1 --smtp-port 1025 \
      --password "<bridge-password>" \
      --smtp-tls --ca ~/bridge/cert.pem
    • --ca <path> trusts a PEM certificate, such as Bridge's exported cert.pem
    • --smtp-tls forces implicit TLS on a custom SMTP port
    • --insecure skips certificate verification. It is unsafe; prefer --ca
    • --no-tls disables implicit IMAP TLS. STARTTLS is still used when the server offers it
  2. New label rename command — rename a Gmail label in place. The label keeps its ID and all its threads:

    zele label list
    zele label rename Label_12 Bookings
  3. mail search now finds mail you sent — before, search looked only in Inbox. IMAP search now looks in Inbox and Sent; use in:sent or in:inbox for one mailbox. Gmail search with no --folder now searches all mail (spam and trash excluded). Results from both mailboxes are sorted by date across pages. mail list --folder stays on that folder even if the filter has a different in:.

    zele mail search "to:alice@example.com" --limit 20
    zele mail search "in:sent to:alice@example.com" --limit 20
  4. No more hard-wrapped Gmail drafts — Gmail opened text-only drafts in its plain-text composer, which broke long paragraphs into ~78-column lines on send. Plain-text bodies are now sent with an HTML copy, so lines wrap naturally. Text like Bob <bob@example.com> or Vec<String> is no longer mistaken for HTML. This also fixes forwarded drafts losing their line breaks.

  5. mail label --add no longer creates junk labels — labels created or renamed outside zele were not found, so zele created duplicates, and a label ID like Label_12 became a new label with that literal name. zele now matches label IDs as well as names, refreshes the label list before it treats a label as missing, and rejects unknown Label_<n> IDs.

  6. Fixed mail list --filter is:unread — it no longer returns read or sent threads, and it fills --limit with real matches. from is now the sender of the latest message, and threads where you sent the latest message show flags: sent. --label Work resolves label names.

  7. Fixed unreadable non-ASCII IMAP bodies — Chinese (UTF-8, GBK), Latin-1 accents and Windows-1252 punctuation now show correctly in quoted-printable, base64 and raw bodies.

Thanks @georgantas for #15, @yougeqiu for #16, @emanuelet for #18 and #19, and @Cvikli for finding the draft wrapping bug in #17!

zele@0.8.0

Choose a tag to compare

@remorses remorses released this 04 Sep 19:52
  1. Importable mail SDK — apps can list, read, reply, and mutate mail without spawning the CLI. import 'zele' now resolves to dist/index.js:

    import {
      listMailThreads,
      readMailThread,
      messageToMarkdown,
      archiveMail,
      replyMail,
    } from 'zele'
    
    const listed = await listMailThreads({ folder: 'inbox', limit: 20 })
    if (listed instanceof Error) throw listed
    
    const first = listed.threads[0]
    const thread = await readMailThread({
      threadId: first.id,
      account: first.account,
    })
    if (thread instanceof Error) throw thread
    
    console.log(messageToMarkdown(thread.messages[0]))

    The same module also exports starMail, unstarMail, trashMail, markMailRead, markMailUnread, getMailAttachment, plus listAccounts and getClients for the lower-level Gmail and IMAP clients. Functions return Error | T instead of throwing.

  2. IMAP/SMTP --attach now survives send — --attach already reached SMTP, but the Sent-folder copy fell back to a plain-text RFC 822 body because MailComposer is not a public nodemailer export. Zele now compiles one MIME buffer with nodemailer/lib/mail-composer and uses that buffer for both SMTP DATA and IMAP Sent APPEND:

    zele mail send --account you@example.com --to peer@example.com \
      --subject 'Invoice' --body 'Attached.' --attach ./invoice.pdf

zele@0.7.0

Choose a tag to compare

@remorses remorses released this 01 Sep 16:08
  1. Microsoft OAuth for Outlook, Hotmail, and Microsoft 365 — connect Microsoft accounts without an app password:

    zele login microsoft
    zele login microsoft --email you@outlook.com
    zele login --method microsoft

    Outlook.com requires XOAUTH2 for IMAP and SMTP. Zele opens the Microsoft consent page, stores the resulting tokens, and refreshes access tokens before each connection so long-running commands such as mail watch continue after the one-hour token expires.

    In agent and non-TTY environments, browser login starts as a background daemon and returns immediately. Approve the printed URL, then poll for the authenticated account:

    zele login --method microsoft
    zele whoami

    Outlook accounts appear as imap_smtp in zele whoami. Mail commands work through IMAP and SMTP; Google-only labels, filters, and calendar commands remain unavailable.

zele@0.6.0

Choose a tag to compare

@remorses remorses released this 23 Aug 11:02
  1. Safer mail reply recipients — replies used to go to the sender of the last message. When that last message was yours, the reply landed in your own inbox. Recipients now come from the thread: last non-draft message, parsed Reply-To, and walk-back when you sent the last one.

    zele mail reply <thread-id> --dry-run
    zele mail reply <thread-id> --to paul@acme.com --body "…"
    zele mail reply <thread-id> --allow-self --body "…"

    A reply that would only reach you is refused with SelfRecipientError. New zele mail send --thread-id sends into an existing thread with correct In-Reply-To/References.

    zele mail send --thread-id <thread-id> --to paul@acme.com --cc dana@acme.com --body "…"
  2. mail reply requires a prior mail read — agents can no longer reply from a stale thread view. If the latest message changed, the command fails with UnseenLatestError. --dry-run skips the check. --force skips it too.

    zele mail read 18f3b7c9d2a1e4f0
    zele mail reply 18f3b7c9d2a1e4f0 --body "sounds good"
  3. Reliable local database — Prisma + @prisma/adapter-libsql could acknowledge writes then roll them back on exit. That dropped ThreadRead rows and refreshed OAuth tokens. The CLI now uses Drizzle with node:sqlite. Existing ~/.zele/sqlite.db files keep working.

    CLI commands now need Node.js 22.16+.

zele@0.5.0

Choose a tag to compare

@remorses remorses released this 16 Jul 21:33
  1. mail watch exits on first match — mail watch now blocks until the first email matching the filter arrives, prints it, and exits with code 0. This makes it useful for agents that need to wait for a specific email (verification codes, replies, etc.). The --once flag has been removed since the new behavior subsumes it.

    New --timeout flag sets a maximum wait time in seconds. If no matching email arrives before the deadline, the command exits with code 1:

    # wait for a reply from alice
    zele mail watch --filter "is:unread from:alice@example.com"
    
    # wait for a verification code with a 5-minute timeout
    zele mail watch --filter "is:unread subject:verification" --timeout 300
  2. Attachment support for mail reply — send file attachments with replies and draft replies via the --attach flag:

    zele mail reply <threadId> --body "See attached" --attach report.pdf
    zele mail reply <threadId> --body "Draft with attachment" --attach doc.pdf --draft

    Works with both Gmail and IMAP/SMTP accounts. The --attach flag is repeatable.

  3. Shell completions and goke framework alignment — install tab completions for zsh and bash via zele completions install. AI agents (Claude, Cursor, Codex, etc.) are detected automatically and skip interactive prompts. Destructive commands now require --force in non-TTY/agent mode.

  4. Interactive IMAP login — zele login imap is now fully interactive in TTY mode: prompts for email, shows a provider preset selector (Fastmail, Gmail, Outlook, Custom) that auto-fills IMAP/SMTP hosts and ports, and prompts for password with masked input.

  5. Interactive prompts for cal respond and logout — cal respond prompts for accept/decline/tentative when --status is omitted. logout shows an account selector when multiple accounts are logged in.

  6. Atomic schema initialization — schema DDL and column migrations now run inside a single BEGIN IMMEDIATE transaction, acquiring the write lock once instead of 13+ separate transactions. Eliminates SQLITE_BUSY errors when multiple CLI processes start concurrently.

  7. Fixed transient SQLITE_BUSY errors — concurrent process access no longer causes intermittent database busy errors. Thanks @Cyberlane for #13!

Closes #14

zele@0.4.0

Choose a tag to compare

@remorses remorses released this 22 Jun 07:28
  1. Run CLI under Node.js — all CLI subcommands (mail, cal, label, etc.) now work with plain Node.js v18+. The TUI auto-spawns bun when running under Node; if bun isn't installed, it shows install instructions:

    # CLI-only (no bun needed)
    npm install -g zele
    zele mail list --limit 100
    
    # Full TUI support
    bun install -g zele
    zele
  2. Fixed child signal propagation — when the TUI is terminated by a signal (Ctrl+C, SIGTERM), the parent process now re-raises the signal so the shell sees the correct exit status (130 for SIGINT, 143 for SIGTERM) instead of a generic exit code 1.

  3. Preserved HTML comments in email rendering — HTML comments embedded in emails (like <!-- metadata -->) now pass through the HTML-to-markdown conversion instead of being stripped.

  4. Fixed @opentui/react version mismatch — bumped @opentui/core and @opentui/react from ^0.1.88 to ^0.2.12 to match termcast peer deps. Fresh installs were resolving an incompatible 0.1.x version, causing "Renderer not found" and React dispatcher crashes on TUI launch.

Fixes #12

zele@0.3.21

Choose a tag to compare

@remorses remorses released this 21 Apr 18:32
  1. Polished interactive prompts — replaced raw readline prompts with @clack/prompts across auth, draft, calendar, and label commands. All confirmation dialogs and selects now have a clean, styled UI.

  2. --method flag on zele login — skip the interactive prompt for non-interactive environments:

    zele login --method google
    zele login --method imap

    Non-TTY environments without --method now exit with a clear message instead of hanging.

  3. --limit replaces --max on mail list, mail search, cal events, and draft list:

    zele mail list --filter "is:unread" --limit 100
    zele mail search "from:github" --limit 100
    zele cal events --limit 100
  4. --filter replaces --query on cal events, draft list, and mail watch — now consistent with mail list:

    zele cal events --filter "standup"
    zele draft list --filter "invoice"
    zele mail watch --filter "from:github"

zele@0.3.20

Choose a tag to compare

@remorses remorses released this 12 Apr 12:17
  1. --draft flag on mail reply and mail forward — save as draft instead of sending immediately:

    # Draft a reply with proper threading headers
    zele mail reply <threadId> --body "sounds good" --draft
    
    # Draft a reply-all
    zele mail reply <threadId> --body "sounds good" --all --draft
    
    # Draft a forward
    zele mail forward <threadId> --to colleague@example.com --draft

    Full threading headers (In-Reply-To, References) are set so the draft appears inside the original thread in Gmail. IMAP accounts write the headers into the raw MIME appended to the Drafts folder.

  2. draft update <draftId> — update an existing draft without touching unchanged fields:

    # Update just the body
    zele draft update <draftId> --body "updated text"
    
    # Pipe new body from stdin
    echo "new body" | zele draft update <draftId> --body-file -
    
    # Change recipients and subject
    zele draft update <draftId> --to new@example.com --subject "New subject"

    Thread association and send-as alias are preserved automatically. IMAP uses delete + re-append (no native update in IMAP).

  3. mail unsubscribe <threadId> — unsubscribe from mailing lists using RFC 2369 / RFC 8058 headers:

    zele mail unsubscribe <threadId>           # auto: one-click → mailto → url
    zele mail unsubscribe <threadId> --dry-run # preview only
    zele mail unsubscribe <threadId> --via mailto
    zele mail unsubscribe <threadId> --then archive

    One-click only triggers automatically when the message passes DKIM (spoofing guard). SSRF guard blocks requests to localhost and internal ranges.

  4. can_unsubscribe and one_click in mail list / mail search — every thread row shows whether a standardized unsubscribe mechanism is present.

  5. Fixed timeout on slow unsubscribe endpoints — one-click POST now times out after 10 s.

  6. mail unsubscribe tolerates 3xx redirects — many real senders redirect after processing the POST. Treated as success with a warning instead of failing.

zele@0.3.17

Choose a tag to compare

@remorses remorses released this 06 Apr 22:20
  1. IMAP/SMTP account support — connect any email provider (Fastmail, Outlook, Gmail app passwords, or any IMAP server) alongside existing Google OAuth accounts:

    # Fastmail
    zele login imap \
      --email you@fastmail.com \
      --imap-host imap.fastmail.com --imap-port 993 \
      --smtp-host smtp.fastmail.com --smtp-port 465 \
      --password "your-app-password"
    
    # Outlook
    zele login imap \
      --email you@outlook.com \
      --imap-host outlook.office365.com --imap-port 993 \
      --smtp-host smtp-mail.outlook.com --smtp-port 587 \
      --password "your-password"
    
    # IMAP-only (no sending)
    zele login imap \
      --email reader@example.com \
      --imap-host imap.example.com \
      --password "pass"

    All read/write mail commands work with IMAP accounts: mail list, mail read, mail search, mail send, mail reply, mail forward, mail star, mail archive, mail trash, mail mark-read, draft list, draft send, etc. Google-only features (labels, filters, calendar) show a clear error when used with IMAP accounts.

    mail list and mail search merge results from Google and IMAP accounts automatically. RFC 6154 special-use folder detection means sent/drafts/trash resolve correctly across providers (including Outlook "Sent Items", localized Gmail folders, etc.).

    whoami shows account type and capabilities for each connected account.

  2. Email authentication verification (SPF/DKIM/DMARC) — mail read now shows structured auth verdicts per message:

    auth: spf=pass dkim=pass dmarc=pass ✓
    

    Use --verify to see the raw Authentication-Results header. The authentic boolean field is useful for agents acting on sensitive emails (e.g. subscription cancellations):

    zele mail read <thread-id> | yq '.messages[0].auth.authentic'