Open-source, local-first Node.js CLI that helps you declutter your Gmail inbox using rule-based logic plus optional LLM-assisted classification.
🎬 Demo GIF placeholder — coming soon.
- Dry-run by default. No command changes your mailbox unless you pass
--confirm. - No permanent deletes. "Trash" only moves messages to Gmail's Trash.
- Email bodies are never read, stored, or transmitted. The tool fetches
metadata only (sender, subject, date, labels, size) via Gmail's
format=metadata. - LLM classification is opt-in (
--llm+ your ownANTHROPIC_API_KEY) and sends subject line + sender only to the Anthropic API. Nothing sent is persisted. (A--full-contentflag is accepted for forward-compatibility but is not active in v1 — subject-line-only is always used.) - Unsubscribe helper never auto-unsubscribes — it only surfaces
List-Unsubscribeheaders for your manual review.
Only the minimum two scopes:
| Scope | Why |
|---|---|
https://www.googleapis.com/auth/gmail.modify |
Read message metadata and apply label changes / archive / move-to-trash. Does not allow permanent deletion. |
https://www.googleapis.com/auth/gmail.labels |
Create/list the AI-Cleaner/* labels used for categorization. |
No https://mail.google.com/ full-access scope is ever requested.
Your OAuth refresh token is stored encrypted (AES-256-GCM) at
~/.gmail-ai-cleaner/token.enc, keyed by a machine-local secret file. See
SECURITY.md for details and limitations.
- Create a Google Cloud project at https://console.cloud.google.com/.
- Enable the Gmail API for that project (APIs & Services → Library).
- Configure the OAuth consent screen (External, test users = your own address).
- Create an OAuth client ID of type "Desktop app" and download the JSON as
credentials.json. - Keep
credentials.jsonprivate; never commit it.
For API quotas and rate limits, see Google's official Gmail API quota documentation — this project intentionally does not restate those numbers.
git clone https://github.com/Munawarx/gmail-ai-cleaner-cli.git
cd gmail-ai-cleaner-cli
npm install
npm run build# 1. Authorize (opens a consent URL; token stored encrypted locally)
node dist/index.js login --credentials ./credentials.json
# 2. Scan + classify + preview (DRY-RUN — nothing changes)
node dist/index.js scan --credentials ./credentials.json --max 300
# 3. Optional: LLM pass for ambiguous messages (subject-line-only)
ANTHROPIC_API_KEY=sk-ant-... node dist/index.js scan --credentials ./credentials.json --llm
# 4. Export a report
node dist/index.js scan --credentials ./credentials.json --export report.json
# 5. Actually execute the previewed actions
node dist/index.js scan --credentials ./credentials.json --confirm
# Unsubscribe helper (surfaces List-Unsubscribe headers; manual action only)
node dist/index.js unsubscribe-helper --credentials ./credentials.json| Category | Rule signals | Default action |
|---|---|---|
| promotions | Gmail CATEGORY_PROMOTIONS, promo keywords |
archive |
| newsletters | "newsletter/digest/weekly", List-Unsubscribe |
label AI-Cleaner/Newsletters |
| likely-spam | spammy sender/subject patterns, SPAM label |
trash |
| receipts | "receipt/invoice/order confirmation" | label AI-Cleaner/Receipts |
| unread-old | unread and older than 90 days | archive |
| large-attachments | ≥5 MiB with attachment | label AI-Cleaner/Large |
| ambiguous / keep | everything else | none |
Auto-reply/auto-send, background daemons, and any cloud/multi-user backend are explicitly out of scope.
npm test # vitest
npm run build # tscSee CONTRIBUTING.md. Security reports: SECURITY.md.
MIT — see LICENSE.