gmail-cleanup
Reclaim your Gmail inbox — safely. One-click unsubscribe (RFC 8058), declarative filter management, scheduled autopilot, and a hard-coded KEEP list that physically cannot unsubscribe you from your bank.
$ gmail-cleanup autopilot
🤖 gmail-cleanup autopilot — full inbox cleanup
━━━ Phase 1/4: Apply Gmail filters ━━━
✅ 8 upgraded, 4 created (all archive + mark read)
━━━ Phase 2/4: Unsubscribe noise senders (last 30d, min-count 2) ━━━
✅ 25 unsubscribed via RFC 8058 one-click POST, 61 inbox messages archived
━━━ Phase 3/4: Mark archived-unread as read ━━━
✅ 4,710 messages marked read
━━━ Phase 4/4: Verify previously-unsubscribed senders ━━━
⚠️ 2 stuck (re-run with --escalate to auto-block)
✅ 18 silent (unsubs stuck)
🎉 Autopilot complete. Inbox: 144 (was 7,283) · Unread: 150 (was 4,858)
✨ Why it's different
| Other tools | gmail-cleanup | |
|---|---|---|
| Safety | Best-effort | KEEP list refuses to unsub from banks, .gov, healthcare |
| Unsubscribe | Click the link in the email | RFC 8058 one-click POST — the modern standard |
| Real humans | At risk of getting archived | Whitelist-protected — starred, important, spam-shielded |
| Reversibility | Often destructive | Archive over delete by default — recoverable from All Mail |
| Auditability | Black box | 84-test safety net + structured logs + state file |
| Automation | Manual, daily | One-command autopilot, optional daily scheduler (macOS) |
📦 Installation
End users — isolated install via pipx (recommended):
pipx install gmail-inbox-cleanup
The PyPI package is gmail-inbox-cleanup (the gmail-cleanup name on PyPI belongs to an unrelated project); the command it installs is gmail-cleanup.
Developers — clone and edit:
git clone https://github.com/bgorzelic/gmail-cleanup.git
cd gmail-cleanup
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
Requirements: Python 3.11+ and a Google Cloud project with the Gmail API enabled (the setup wizard walks you through both).
💡 Hitting OAuth errors? See
TROUBLESHOOTING.md. The most common gotcha (consent screen / test users for unverified apps) is handled by the setup wizard in v0.5.1+.
🚀 Quick start
# First time — interactive wizard handles GCP, OAuth, config in 7 steps:
gmail-cleanup setup
# Daily use — one command runs the full cleanup pipeline:
gmail-cleanup autopilot
# Glance at inbox health:
gmail-cleanup status
# Two weeks later — escalate any unsubs that didn't stick:
gmail-cleanup autopilot --escalate
--dry-run previews any destructive command. --quiet for cron-friendly silent runs. --verbose for debug.
🧠 How it works
Every sender falls into one of three buckets:
┌──────────────────────────────────┐
inbox mail ──▶│ Real human (humans.yaml) │──▶ ⭐ star + important + spam-shield
├──────────────────────────────────┤
│ Must-keep automation (keep.yaml) │──▶ 🔒 NEVER unsubscribe (safety rail)
│ banks, .gov, health, brokerage │
├──────────────────────────────────┤
│ Noise (kill.yaml + auto-discover)│──▶ 🗑 one-click unsub + archive
└──────────────────────────────────┘
Tune the buckets by editing the YAML files in ~/.gmail_cli/lists/ — no Python required.
🛠 The autopilot pipeline
gmail-cleanup autopilot
│
├── Phase 1 filters apply Declare categorization rules to Gmail (idempotent)
├── Phase 2 unsubscribe Find recent noise, hit RFC 8058 one-click, archive
├── Phase 3 mark-read Clear the archived-but-unread backlog
└── Phase 4 verify Audit previous unsubs; --escalate to block stuck ones
Each phase also works standalone. Autopilot is the convenience composition.
📖 Commands
| Command | What it does |
|---|---|
setup |
Interactive 7-step wizard: GCP credentials → OAuth → account registration. Start here. |
autopilot |
Full pipeline. The daily driver. --days N / --min-count K tune the unsubscribe phase; --email-summary emails the report to you (try it with --dry-run); --all-accounts runs across every configured Gmail. |
status |
Dashboard: live counts, filter inventory, list sizes, 7-day history |
stats |
Inbox count, unread, storage, oldest email |
top-senders --days N |
Rank senders by volume |
subscriptions |
Find senders with List-Unsubscribe headers |
unsubscribe --days N --min-count K |
One-click unsubscribe noise senders + archive their mail |
mark-read --query Q |
Bulk-mark messages as read (default: archived-but-unread backlog) |
verify [--grace-days N] [--escalate] |
Check whether senders kept mailing after you unsubscribed (plus a 2-day grace period); auto-block stuck senders |
attachments [--archive|--delete] |
Find oversized old emails, rank by bytes |
filters apply / list |
Create/upgrade/list Gmail filters |
config show / init |
Manage ~/.gmail_cli/config.yaml |
accounts list / add / remove |
Manage multi-account roster |
schedule install / uninstall / status |
Daily launchd-scheduled autopilot (macOS) |
archive / delete / label |
Bulk operations by sender / category / label / query / age |
All destructive commands prompt for confirmation unless you pass --yes. Global flags: --quiet, --verbose, --all-accounts.
🔐 Safety model
Four YAML lists govern behavior. Each one is the packaged seed (shipped inside the package) merged with your own copy in ~/.gmail_cli/lists/<name>.yaml. Edit your copies to tune the tool — no Python required. Everything the tool writes goes to ~/.gmail_cli/lists/, never into the installed package.
| File | Used by | Match | Seed ships | Behavior |
|---|---|---|---|---|
keep.yaml |
unsubscribe |
Substring | Populated | If sender matches, the unsubscribe is refused. Banks, healthcare, .gov, security senders. |
kill.yaml |
unsubscribe, filters apply |
Substring | Empty | Forces unsubscribe + archive regardless of message-count threshold |
humans.yaml |
filters apply |
Exact email | Empty | Star + mark important + spam-protect. |
unsubbed.yaml |
filters apply, verify |
Exact email | Empty | Anti-resurrection — auto-archive if a previously-unsubscribed sender tries to come back. Written automatically after each successful unsubscribe. |
The unsubscribe flow prefers RFC 8058 one-click POST (the standard Gmail/Apple now require for bulk senders). Falls back to GET, then mailto. Senders without any List-Unsubscribe header are skipped, not silently archived — that's a guard against accidentally archiving a real person.
See gmail_cleanup/lists/README.md for the conflict-resolution rules between the four files.
👥 Multi-account
gmail-cleanup accounts add work@company.com --label work
gmail-cleanup accounts add personal@gmail.com --label personal
gmail-cleanup accounts list
# Run the same command across every configured account:
gmail-cleanup autopilot --all-accounts
gmail-cleanup stats --all-accounts
gmail-cleanup verify --all-accounts
Partial-failure semantics: if one account errors, the others still run. Failures are reported at the end. Exit code = number of failed accounts (0 = all clean).
🤖 Daily autopilot (macOS)
# Install a launchd job that runs autopilot every day at 08:00 local:
gmail-cleanup schedule install --time 08:00 --escalate
# Check it's wired up:
gmail-cleanup schedule status
# Remove it:
gmail-cleanup schedule uninstall
Linux (systemd) and Windows (Task Scheduler) integrations are on the roadmap.
🛠 Development
pip install -e ".[dev]"
pytest # 200+ tests, <1s
ruff check . # lint
ruff format . # format
CI runs pytest on Python 3.11/3.12/3.13 for every push and PR.
📚 Documentation
ARCHITECTURE.md— 5-minute tour of the codebaseTROUBLESHOOTING.md— common first-run issuesCHANGELOG.md— version historyHANDOFF.md— current session stateROADMAP.md— what's plannedCONTRIBUTING.md— how to contributegmail_cleanup/lists/README.md— list conflict-resolution rulesdocs/superpowers/specs/— design specs for major featuresdocs/superpowers/plans/— implementation plans
🤝 Contributing
Issues, PRs, and discussion all welcome. Start with CONTRIBUTING.md — it covers the safety invariants you must not break.
The cardinal rule: the tool's job is to never unsubscribe you from your bank. Anything that loosens the KEEP list semantics for convenience will be rejected.
📄 License
MIT — see LICENSE.
Built by Brian Gorzelic. Safety-tested on a 7,283-email backlog before any of you used it.
Release files for gmail-inbox-cleanup 0.6.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gmail_inbox_cleanup-0.6.0.tar.gz | 70.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gmail_inbox_cleanup-0.6.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 117.2 kB
Release files / gmail_inbox_cleanup-0.6.0.tar.gz
| Download URL | gmail_inbox_cleanup-0.6.0.tar.gz |
|---|---|
| Size | 70.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3fea261ba163b671a42bab853bc7edc3ac93d59c225ebbe6bfe48846a66b95c1
|
|
BLAKE2b-256 checksum How to use checksums |
d74b35e528ac054dbfb8d1d2d6935b50c0fe710f901452379514197bf90cd020
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency logRelease files / gmail_inbox_cleanup-0.6.0-py3-none-any.whl
| Download URL | gmail_inbox_cleanup-0.6.0-py3-none-any.whl |
|---|---|
| Size | 47.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
53d6dad05b91492f14d827f12c99a2a5b0e828e2895d031d41eb832d4b3c737c
|
|
BLAKE2b-256 checksum How to use checksums |
a83dc18ae5d16632affb7b4f60e429605e372f6b2703b475cc286d6b6647429b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.
Transparency log