<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Giulio Alberello</title>
    <description>The latest articles on DEV Community by Giulio Alberello (@mrpickles00).</description>
    <link>https://dev.to/mrpickles00</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3997236%2Fa5a05e92-5fbc-4602-8b63-e70fa6944796.png</url>
      <title>DEV Community: Giulio Alberello</title>
      <link>https://dev.to/mrpickles00</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mrpickles00"/>
    <language>en</language>
    <item>
      <title>I built an open-source tool that cleans a decade-old mailbox with local-first AI</title>
      <dc:creator>Giulio Alberello</dc:creator>
      <pubDate>Mon, 22 Jun 2026 15:55:08 +0000</pubDate>
      <link>https://dev.to/mrpickles00/i-built-an-open-source-tool-that-cleans-a-decade-old-mailbox-with-local-first-ai-2g1m</link>
      <guid>https://dev.to/mrpickles00/i-built-an-open-source-tool-that-cleans-a-decade-old-mailbox-with-local-first-ai-2g1m</guid>
      <description>&lt;h2&gt;
  
  
  The mailbox that ate ten years
&lt;/h2&gt;

&lt;p&gt;I'd had the same Gmail account for ~14 years. Opening it had become depressing: ~40,000 newsletters I never read, spam from services that no longer exist, receipts and notifications from tools I stopped using in 2016. The kind of mailbox you don't clean because the &lt;em&gt;act of cleaning&lt;/em&gt; is the chore.&lt;/p&gt;

&lt;p&gt;I'm a software architect (~10 years), so my instinct was: surely there's a tool. There wasn't - not really. There are scattered Python scripts and a few half-abandoned utilities, but nothing that felt like a complete, &lt;strong&gt;safe&lt;/strong&gt; product: something that lets you say "show me everything from these 40 senders, tell me how many there are, and let me preview before I touch anything" - or better, "just figure out what's junk and clean it for me, without shipping my whole inbox to a cloud."&lt;/p&gt;

&lt;p&gt;So I wrote a few lines of Python over IMAP, just for myself. Then it kept growing, and I decided to finish it properly and open-source it: &lt;strong&gt;IMAP Cleanup Tool&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;pip install "imap-cleanup-tool[web,ai]"&lt;/code&gt; then &lt;code&gt;imap-cleanup-tool-web&lt;/code&gt;&lt;br&gt;
Repo: &lt;a href="https://github.com/mrpickles007/imap-cleanup-tool" rel="noopener noreferrer"&gt;https://github.com/mrpickles007/imap-cleanup-tool&lt;/a&gt;&lt;br&gt;
Site: &lt;a href="https://imapcleanuptool.com" rel="noopener noreferrer"&gt;https://imapcleanuptool.com&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wkvk28o4pr4se2ckwox.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0wkvk28o4pr4se2ckwox.gif" alt="IMAP Cleanup Tool demo" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The headline: AI Cleanup that runs local-first
&lt;/h2&gt;

&lt;p&gt;The feature I'm proudest of is &lt;strong&gt;AI Cleanup&lt;/strong&gt;: hand "which of these do I actually want?" to a model, safely - and efficiently. The key design choice is that it works on &lt;strong&gt;aggregated per-sender statistics, not your individual emails&lt;/strong&gt;. It never feeds a whole mailbox to an LLM (that would be slow and make the token count explode); a local heuristic does the bulk of the work, and only a short list of borderline &lt;strong&gt;senders&lt;/strong&gt; ever reaches the model.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A local heuristic scores every sender&lt;/strong&gt; 0-10 from signals read on your machine: &lt;code&gt;List-Unsubscribe&lt;/code&gt;, the share of unread messages, send frequency, &lt;code&gt;Precedence: bulk&lt;/code&gt;, and sender patterns (&lt;code&gt;noreply@&lt;/code&gt;, &lt;code&gt;newsletter@&lt;/code&gt;). Weights are calibrated and tunable. This step never leaves your machine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The LLM only sees the borderline cases.&lt;/strong&gt; Only senders at or above your threshold go to a model, with a few &lt;strong&gt;sample subjects&lt;/strong&gt; each (plus stats) - &lt;strong&gt;never the message body&lt;/strong&gt;. It returns strict JSON verdicts (pydantic-validated, retried on bad output) saying what to delete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You stay in control.&lt;/strong&gt; A &lt;strong&gt;Report only&lt;/strong&gt; mode shows exactly what &lt;em&gt;would&lt;/em&gt; be deleted and changes nothing; actually deleting is a separate, deliberate step (off by default).&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;💸 &lt;strong&gt;What does it cost?&lt;/strong&gt; In my testing, cleaning a &lt;strong&gt;~40,000-message&lt;/strong&gt; Gmail with &lt;strong&gt;gpt-4o-mini&lt;/strong&gt; cost about &lt;strong&gt;€0.03&lt;/strong&gt; and removed &lt;strong&gt;~13,000 emails in ~5 minutes&lt;/strong&gt;. Only senders over the threshold ever hit the LLM (a few subjects each), so cost stays tiny - and a local Ollama model is free. It even gets &lt;strong&gt;cheaper the more you run it&lt;/strong&gt;: senders already saved as spam are skipped from the model on later runs, so each cleanup sends fewer addresses than the last.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The two things that make this something I'd actually run on my own inbox:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local-first.&lt;/strong&gt; Configure a free local model via &lt;strong&gt;Ollama&lt;/strong&gt; (&lt;code&gt;ollama/llama3&lt;/code&gt; works out of the box) and &lt;em&gt;nothing&lt;/em&gt; leaves your machine - not the subjects, not the stats, nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BYOA (Bring Your Own API key).&lt;/strong&gt; Prefer a cloud model? Plug in OpenAI, OpenRouter, or anything litellm supports with your own key. Optional per-model cost tracking tells you what each run cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the prompt has an explicit safeguard: it must KEEP anything that looks like online orders/receipts, appointments/bookings, medical/health, travel, banking/tax, security/2FA, or personal mail - only obvious bulk (newsletters, promotions, notifications) is ever marked deletable. The senders it flags also land in a per-account &lt;strong&gt;"spam addresses"&lt;/strong&gt; list, so you can report them to the server and push their &lt;em&gt;future&lt;/em&gt; mail straight to spam - or &lt;strong&gt;unsubscribe&lt;/strong&gt; from them in bulk (see the next section).&lt;/p&gt;

&lt;h2&gt;
  
  
  One-click unsubscribe from newsletters
&lt;/h2&gt;

&lt;p&gt;Deleting is half the battle; the other half is making the junk stop arriving. From that same &lt;strong&gt;spam addresses&lt;/strong&gt; list you can &lt;strong&gt;bulk-unsubscribe&lt;/strong&gt; from newsletters. The tool captures each sender's &lt;code&gt;List-Unsubscribe&lt;/code&gt; header during an AI report, then, for the senders you select, does the unsubscribe &lt;strong&gt;automatically&lt;/strong&gt; where the standard allows it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;&lt;code&gt;mailto:&lt;/code&gt;&lt;/strong&gt; unsubscribe, sent for you from your active SMTP profile, or&lt;/li&gt;
&lt;li&gt;an &lt;strong&gt;RFC 8058 one-click&lt;/strong&gt; HTTPS POST.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Senders that only offer a plain confirmation-page link can't be automated - so instead of opening a wall of browser tabs, the list &lt;strong&gt;filters itself to those leftovers&lt;/strong&gt; and you open each with its per-row &lt;code&gt;link ↗&lt;/code&gt;. Honest framing: &lt;em&gt;automatic for most, open-the-page for the rest&lt;/em&gt;. And it's a deliberate, outbound action meant for &lt;strong&gt;real newsletters&lt;/strong&gt; - never for actual spam, where unsubscribing just confirms your address is live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modern auth (OAuth2) where you need it
&lt;/h2&gt;

&lt;p&gt;Microsoft has turned off password login over IMAP/SMTP for personal and 365 accounts, which quietly breaks a lot of older tools. So there's a &lt;strong&gt;"Sign in with Microsoft"&lt;/strong&gt; built in: an OAuth2 &lt;strong&gt;device-code&lt;/strong&gt; flow (open a URL, type a short code) that stores only an &lt;strong&gt;encrypted refresh token&lt;/strong&gt; - no password - and authenticates with &lt;strong&gt;XOAUTH2&lt;/strong&gt;. It works for reading mail (IMAP), for sending the notification emails (SMTP), and even for &lt;strong&gt;unattended scheduled jobs&lt;/strong&gt;, because access tokens are minted silently from the refresh token. I implemented it with just the standard library (&lt;code&gt;urllib&lt;/code&gt; / &lt;code&gt;imaplib&lt;/code&gt; / &lt;code&gt;smtplib&lt;/code&gt;), no vendor SDK, and the device-code flow has no localhost redirect, so it works headless / over SSH.&lt;/p&gt;

&lt;p&gt;Every other provider (Gmail, Yahoo, iCloud, your own domain) just connects with a normal or app password. And the OAuth providers live in a &lt;strong&gt;JSON config file&lt;/strong&gt; (endpoints, client id, optional secret, scope), so adding Google - or pointing the tool at your &lt;strong&gt;own organization's app registration&lt;/strong&gt;, the setup you'd want for an enterprise deployment - is a config edit, not a code change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Still a precise manual tool when you want control
&lt;/h2&gt;

&lt;p&gt;AI is optional. Under the hood it's still a complete, scriptable IMAP cleaner:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;See who's flooding you.&lt;/strong&gt; Export a CSV of every sender and how many emails each sent, ranked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Match by anything.&lt;/strong&gt; Sender, domain, or &lt;strong&gt;nested AND/OR rules&lt;/strong&gt; built in a visual builder - no query syntax to memorize.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Count before you touch&lt;/strong&gt;, with &lt;strong&gt;dry-run by default&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move instead of delete.&lt;/strong&gt; Send matches to another folder (or a Gmail label), create/delete folders on the fly. Great for archiving instead of nuking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast on huge folders&lt;/strong&gt; via server-side IMAP search, with &lt;strong&gt;batched deletes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email notifications&lt;/strong&gt;: get a mail when a run finishes, with the AI report attached as CSV.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web UI and CLI&lt;/strong&gt; (full AI parity on the CLI), plus a guided tour on first run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling&lt;/strong&gt; through your OS (Task Scheduler / cron) - including scheduled AI jobs - for recurring cleanups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-only.&lt;/strong&gt; Your credentials never leave your machine. No cloud, no tracking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A 60-second tour
&lt;/h2&gt;

&lt;p&gt;Let the AI build a report (nothing is deleted):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;imap-cleanup-tool --host imap.gmail.com --user you@gmail.com \
    --ai-cleanup --ai-report-only --ai-report-csv report.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Find your worst offenders the manual way:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;imap-cleanup-tool --host imap.gmail.com --user you@gmail.com --list-senders --save-senders senders.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Preview a rule-based cleanup (changes nothing):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;imap-cleanup-tool --host imap.gmail.com --user you@gmail.com \
    --targets junk.txt --dry-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Archive instead of delete - move matches into a folder/label:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;imap-cleanup-tool --host HOST --user USER --create-folder "Archive/2013"
imap-cleanup-tool --host HOST --user USER --targets old.txt \
    --move --dest-folder "Archive/2013" --dry-run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or just use the web UI - &lt;code&gt;imap-cleanup-tool-web&lt;/code&gt; - if you'd rather click.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I actually care about: how it's built
&lt;/h2&gt;

&lt;p&gt;This is where I let the engineering show, because the &lt;em&gt;way&lt;/em&gt; something is built is the difference between a script and a tool you'd trust with your mailbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A UI-agnostic core.&lt;/strong&gt; All IMAP logic lives in one module that knows nothing about argument parsing or HTML. It uses only the Python standard library (&lt;code&gt;imaplib&lt;/code&gt;), which means the CLI has &lt;strong&gt;zero runtime dependencies&lt;/strong&gt;; the web UI (&lt;code&gt;[web]&lt;/code&gt;) and the AI features (&lt;code&gt;[ai]&lt;/code&gt;) are optional, lazily-imported extras. Both front-ends call the same core, so there's no logic duplicated and no "works in the UI but not the CLI" drift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The AI layer is provider-agnostic and defensive.&lt;/strong&gt; It uses litellm so local (Ollama) and cloud models share one path; LLM calls are batched with timeouts and cooperative cancellation; verdicts are validated with pydantic and retried before giving up. Only subjects and stats are ever sent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rules as a serializable tree.&lt;/strong&gt; A rule is a &lt;code&gt;Condition&lt;/code&gt;/&lt;code&gt;Group&lt;/code&gt; tree (AND/OR, arbitrarily nestable) that compiles to an IMAP &lt;code&gt;SEARCH&lt;/code&gt; string. The visual builder and the CLI's &lt;code&gt;--rule&lt;/code&gt; grammar both produce the &lt;em&gt;same&lt;/em&gt; tree - so a rule you build with clicks can be saved into a scheduled job and run headless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safety is a design constraint, not a footnote.&lt;/strong&gt; Dry-run is the default everywhere. Deletion flags messages and only &lt;code&gt;--expunge&lt;/code&gt; (or emptying a folder) removes them for good - and that expunge is &lt;strong&gt;batched&lt;/strong&gt;, after I hit a real &lt;code&gt;EXPUNGE =&amp;gt; System Error&lt;/code&gt; emptying a Trash of ~11.5k messages in one shot. You can't move a folder into itself; system folders (Trash, Sent, ...) can't be deleted - detected via IMAP special-use flags, not by name, so it works on localized and Gmail mailboxes too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Boring-but-important hygiene.&lt;/strong&gt; 270+ automated tests; CI runs the suite on Python 3.10 through 3.14; releases go to PyPI via Trusted Publishing (no API tokens), each tag auto-creating a GitHub release. I tested it end-to-end on real Gmail and Outlook/Microsoft accounts and on a mailbox on my own domain.&lt;/p&gt;

&lt;p&gt;A note on transparency: I designed and architected this, and used an AI assistant to speed up writing code. I leaned on the test suite and CI rather than trusting generated code blindly - and the code is all there to read. (That's separate from the product's own AI Cleanup feature.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Python 3.10+; [web,ai] adds the web UI + AI Cleanup on top of the CLI
pip install "imap-cleanup-tool[web,ai]"
imap-cleanup-tool-web        # opens the local web UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Everything is local. For AI, point it at a local Ollama model (nothing leaves your machine) or paste your own cloud API key. On Gmail, use an app password (not your real password) and you're set. Don't need AI? &lt;code&gt;pip install "imap-cleanup-tool[web]"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it's going / contributing
&lt;/h2&gt;

&lt;p&gt;It's AGPL-3.0, with issue templates and a contributing guide. I'd love help with edge cases on non-Gmail providers, more rule fields, translations, and feedback on which local models work well for the AI step. If you try it, a GitHub star genuinely helps, and bug reports/feature requests are read and answered.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/mrpickles007/imap-cleanup-tool" rel="noopener noreferrer"&gt;https://github.com/mrpickles007/imap-cleanup-tool&lt;/a&gt;&lt;br&gt;
Site: &lt;a href="https://imapcleanuptool.com" rel="noopener noreferrer"&gt;https://imapcleanuptool.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. I hope it gives you back a clean inbox - feedback, issues and PRs are all read and answered.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>ai</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
