tidy-ai 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. tidy_ai-0.2.0/LICENSE +21 -0
  2. tidy_ai-0.2.0/PKG-INFO +214 -0
  3. tidy_ai-0.2.0/README.md +181 -0
  4. tidy_ai-0.2.0/pyproject.toml +51 -0
  5. tidy_ai-0.2.0/setup.cfg +4 -0
  6. tidy_ai-0.2.0/tests/test_act.py +268 -0
  7. tidy_ai-0.2.0/tests/test_codex_baseline.py +83 -0
  8. tidy_ai-0.2.0/tests/test_collector.py +171 -0
  9. tidy_ai-0.2.0/tests/test_discovery.py +90 -0
  10. tidy_ai-0.2.0/tests/test_escalate.py +74 -0
  11. tidy_ai-0.2.0/tests/test_evidence_store.py +99 -0
  12. tidy_ai-0.2.0/tests/test_experiment.py +121 -0
  13. tidy_ai-0.2.0/tests/test_gmail.py +254 -0
  14. tidy_ai-0.2.0/tests/test_inventory.py +204 -0
  15. tidy_ai-0.2.0/tests/test_judge.py +194 -0
  16. tidy_ai-0.2.0/tests/test_label_retest.py +35 -0
  17. tidy_ai-0.2.0/tests/test_mail.py +363 -0
  18. tidy_ai-0.2.0/tests/test_mail_report_html.py +113 -0
  19. tidy_ai-0.2.0/tests/test_mutation.py +162 -0
  20. tidy_ai-0.2.0/tests/test_pilot.py +70 -0
  21. tidy_ai-0.2.0/tests/test_policy.py +120 -0
  22. tidy_ai-0.2.0/tests/test_profile.py +30 -0
  23. tidy_ai-0.2.0/tests/test_rename.py +41 -0
  24. tidy_ai-0.2.0/tests/test_report_html.py +134 -0
  25. tidy_ai-0.2.0/tests/test_review.py +222 -0
  26. tidy_ai-0.2.0/tests/test_setup_check.py +113 -0
  27. tidy_ai-0.2.0/tests/test_ship_fixes.py +100 -0
  28. tidy_ai-0.2.0/tests/test_watch_history.py +44 -0
  29. tidy_ai-0.2.0/tidy/__init__.py +3 -0
  30. tidy_ai-0.2.0/tidy/__main__.py +400 -0
  31. tidy_ai-0.2.0/tidy/collector.py +72 -0
  32. tidy_ai-0.2.0/tidy/discovery.py +28 -0
  33. tidy_ai-0.2.0/tidy/escalate.py +77 -0
  34. tidy_ai-0.2.0/tidy/experiment.py +72 -0
  35. tidy_ai-0.2.0/tidy/gmail.py +314 -0
  36. tidy_ai-0.2.0/tidy/judge.py +165 -0
  37. tidy_ai-0.2.0/tidy/mail.py +260 -0
  38. tidy_ai-0.2.0/tidy/mail_report_html.py +151 -0
  39. tidy_ai-0.2.0/tidy/mutate.py +251 -0
  40. tidy_ai-0.2.0/tidy/pilot.py +40 -0
  41. tidy_ai-0.2.0/tidy/policy.py +93 -0
  42. tidy_ai-0.2.0/tidy/profile.py +53 -0
  43. tidy_ai-0.2.0/tidy/proposal.py +14 -0
  44. tidy_ai-0.2.0/tidy/report_html.py +208 -0
  45. tidy_ai-0.2.0/tidy/review.py +145 -0
  46. tidy_ai-0.2.0/tidy/setup_check.py +137 -0
  47. tidy_ai-0.2.0/tidy/store.py +378 -0
  48. tidy_ai-0.2.0/tidy/watch_history.py +32 -0
  49. tidy_ai-0.2.0/tidy/youtube.py +222 -0
  50. tidy_ai-0.2.0/tidy_ai.egg-info/PKG-INFO +214 -0
  51. tidy_ai-0.2.0/tidy_ai.egg-info/SOURCES.txt +53 -0
  52. tidy_ai-0.2.0/tidy_ai.egg-info/dependency_links.txt +1 -0
  53. tidy_ai-0.2.0/tidy_ai.egg-info/entry_points.txt +3 -0
  54. tidy_ai-0.2.0/tidy_ai.egg-info/requires.txt +4 -0
  55. tidy_ai-0.2.0/tidy_ai.egg-info/top_level.txt +1 -0
tidy_ai-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abhinav Bansal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tidy_ai-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,214 @@
1
+ Metadata-Version: 2.4
2
+ Name: tidy-ai
3
+ Version: 0.2.0
4
+ Summary: Clean up your YouTube subscriptions and Gmail inbox with Jev: an AI judges, plain code sets the limits, you approve.
5
+ Author: Abhinav Bansal
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/abhibansal60/tidy
8
+ Project-URL: Documentation, https://github.com/abhibansal60/tidy#readme
9
+ Project-URL: Issues, https://github.com/abhibansal60/tidy/issues
10
+ Project-URL: Security, https://github.com/abhibansal60/tidy/blob/main/SECURITY.md
11
+ Project-URL: Changelog, https://github.com/abhibansal60/tidy/blob/main/CHANGELOG.md
12
+ Keywords: gmail,inbox-zero,email-cleanup,youtube-subscriptions,jev,typesafe,ai-agents,cli
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: End Users/Desktop
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Communications :: Email
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.11
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: google-auth<3,>=2.40
29
+ Requires-Dist: google-auth-oauthlib<2,>=1.2
30
+ Requires-Dist: requests<3,>=2.32.4
31
+ Requires-Dist: typesafe-sdk<1,>=0.7
32
+ Dynamic: license-file
33
+
34
+ # Tidy
35
+
36
+ **Clean up your Gmail inbox and your YouTube subscriptions with AI, without letting AI loose on your account.**
37
+
38
+ [Jev](https://docs.typesafe.ai) reads each email or channel and gives a typed verdict in well under a second. Plain,
39
+ tested code decides what that verdict is allowed to do, and nothing risky happens until you say so.
40
+
41
+ [![CI](https://github.com/abhibansal60/tidy/actions/workflows/ci.yml/badge.svg)](https://github.com/abhibansal60/tidy/actions/workflows/ci.yml)
42
+ [![PyPI](https://img.shields.io/pypi/v/tidy-ai)](https://pypi.org/project/tidy-ai/)
43
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://pypi.org/project/tidy-ai/)
44
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/abhibansal60/tidy/blob/main/LICENSE)
45
+
46
+ <img src="https://raw.githubusercontent.com/abhibansal60/tidy/main/docs/assets/mail_thumbnail.png" alt="Before: 12,158 unread emails. After: sorted, for 45 cents" width="640">
47
+
48
+ | | Gmail | YouTube |
49
+ |---|---|---|
50
+ | **Sorts** | every email into Needs Reply, Updates, Promos, Sales or Spam | every channel by relevance, value and how likely you are to watch it |
51
+ | **Does on its own** | archives bulk mail, only when a second signal agrees | nothing: every change waits for you |
52
+ | **Waits for you** | Trash and Spam, reviewed from a dashboard | unsubscribes and new subscriptions |
53
+ | **Never does** | permanently delete anything, touch starred mail, click unsubscribe links | use browser automation or scrape |
54
+ | **Measured cost** | $0.43 to sort 12,158 unread emails | a fraction of a cent for 110 channels |
55
+
56
+ [Mail demo video (MP4)](https://github.com/abhibansal60/tidy/raw/main/docs/assets/tidy_mail_demo.mp4) ·
57
+ [YouTube demo video (MP4)](https://github.com/abhibansal60/tidy/raw/main/docs/assets/tidy_demo.mp4) ·
58
+ [Cost write-up](https://github.com/abhibansal60/tidy/blob/main/docs/research/opus-5-5-plus-jev-cost.md)
59
+
60
+ ## Install
61
+
62
+ ```bash
63
+ pipx install tidy-ai # or: uv tool install tidy-ai (or: pip install tidy-ai)
64
+ tidy --version
65
+ ```
66
+
67
+ Python 3.11 or newer. The command is `tidy`; `tidy-ai` is the same command, in case another `tidy` (HTML Tidy, which
68
+ macOS ships) comes first on your PATH. Until the first PyPI release: `pipx install git+https://github.com/abhibansal60/tidy`.
69
+
70
+ ## Set up once (about 10 minutes)
71
+
72
+ You need two things that only you can create:
73
+
74
+ 1. **A Google OAuth client.** In [Google Cloud Console](https://console.cloud.google.com/): create a project, enable the
75
+ **Gmail API** (and/or **YouTube Data API v3**), set up the OAuth consent screen in Testing mode with yourself as a
76
+ test user, then create an OAuth client of type **Desktop app** and download its JSON.
77
+ Step-by-step: [docs/agent-setup.md](https://github.com/abhibansal60/tidy/blob/main/docs/agent-setup.md#steps).
78
+ 2. **A TypeSafe API key** for Jev, from [console.typesafe.ai](https://console.typesafe.ai).
79
+
80
+ Then:
81
+
82
+ ```bash
83
+ tidy init --email you@gmail.com --client-secrets ~/Downloads/client_secret_XXXX.json --key-stdin
84
+ # paste the TypeSafe key when asked (it is hidden and stored 0600 in ~/.tidy/.env)
85
+ tidy doctor # checks everything, prints the next command, never prints a secret
86
+ ```
87
+
88
+ ## Clean up Gmail
89
+
90
+ ```bash
91
+ tidy mail-auth # read-only sign-in (a browser opens)
92
+ tidy mail-triage --execute --limit 200 --html inbox.html --json run.json
93
+ ```
94
+
95
+ Open `inbox.html`: every email with its category, why, and what Tidy proposes, plus an **unsubscribe shortlist**
96
+ grouped by sender (links you click yourself). Nothing in Gmail changed yet. When you like what you see:
97
+
98
+ ```bash
99
+ tidy mail-auth --write # a second, separate token that can change labels
100
+ tidy mail-triage --execute --apply --override-gate --limit 200 --json run.json # auto-archives bulk mail only
101
+ tidy mail-act --run run.json # preview the Trash/Spam proposals
102
+ tidy mail-act --run run.json --execute # apply them (Trash is recoverable for 30 days)
103
+ ```
104
+
105
+ `--override-gate` is needed because Tidy cannot yet prove from your own labels that its archiving matches your taste
106
+ ([why](https://github.com/abhibansal60/tidy/blob/main/docs/adr/0005-gated-owner-started-autonomy.md)). Keep it daily with cron:
107
+
108
+ ```cron
109
+ # use the full path from `command -v tidy`; cron has a minimal PATH
110
+ 0 8 * * * /home/you/.local/bin/tidy mail-triage --execute --apply --override-gate --query "in:inbox newer_than:2d" --json ~/.tidy/runs/$(date +\%F).json
111
+ ```
112
+
113
+ ## Clean up YouTube
114
+
115
+ ```bash
116
+ tidy auth # read-only sign-in
117
+ tidy sync # fetch your subscriptions
118
+ tidy collect --all --max-units 400 --execute # recent uploads per channel (YouTube quota: about 300 units)
119
+ tidy judge --schemas titles-desc-v1 --execute # Jev judges every channel
120
+ tidy propose --html review.html # open it: proposals with reasons and the commands to act
121
+ ```
122
+
123
+ <img src="https://raw.githubusercontent.com/abhibansal60/tidy/main/docs/assets/review_page.png" alt="Review page, synthetic data" width="640">
124
+
125
+ To act: `tidy auth --write`, `tidy approve CHANNEL_ID... --note "why"`, `tidy unsubscribe` (dry run), then
126
+ `tidy unsubscribe --execute`. A Google Takeout watch history makes proposals much better: see
127
+ [docs/reference.md](https://github.com/abhibansal60/tidy/blob/main/docs/reference.md).
128
+
129
+ ## Let an agent set it up
130
+
131
+ Paste into Claude Code, Codex or any coding agent:
132
+
133
+ ```text
134
+ Set up Tidy (pipx package tidy-ai) for me. Follow https://github.com/abhibansal60/tidy/blob/main/docs/agent-setup.md
135
+ exactly. Stop and ask me whenever a step needs my Google account, an API key or a browser. Never print my secrets,
136
+ and never run a command with --execute until I say so.
137
+ ```
138
+
139
+ Built for agents: every command prints JSON, `tidy doctor` says exactly what is missing and what to run next,
140
+ every change is a dry run until `--execute`, and secrets go in through a hidden prompt or stdin, never as arguments.
141
+
142
+ ## Safety
143
+
144
+ - **Dry run by default.** Nothing changes your account without `--execute`.
145
+ - **Two tokens.** Reading and changing use separate OAuth tokens; the change token is only requested when you ask.
146
+ - **Gmail cannot be permanently deleted by Tidy.** It never requests the full-access Gmail scope, only `gmail.modify`,
147
+ which can archive, trash and mark spam but cannot erase mail. Trash empties itself after 30 days, as Gmail always does.
148
+ - **AI is never enough on its own.** Auto-archive needs a second, code-owned signal (an unsubscribe header, not being
149
+ addressed to you personally, or Gmail's own Updates/Promotions tab). Starred mail is never proposed for anything.
150
+ - **Rechecked live.** Before any Trash, Spam or unsubscribe, Tidy re-reads the live account and skips anything you have
151
+ moved, starred or changed since.
152
+ - **Capped and budgeted.** Per-run caps, API call budgets checked before the first change, and an audit log.
153
+ - **Private files stay private.** Tokens, config, database, dashboards and run files are written owner-only (0600)
154
+ in `~/.tidy`; `tidy doctor` warns about anything readable by others.
155
+
156
+ Found a problem? See [SECURITY.md](https://github.com/abhibansal60/tidy/blob/main/SECURITY.md).
157
+
158
+ ## What leaves your machine
159
+
160
+ | Sent to | What | When |
161
+ |---|---|---|
162
+ | Google | the API calls you run | always, over HTTPS |
163
+ | TypeSafe (Jev) | per email: subject, sender, first 1,000 characters of text, two yes/no facts; per channel: its name and description, recent video titles, descriptions and lengths, and the interests (and optional viewing habits) you set in your profile | `mail-triage --execute`, `judge --execute` |
164
+ | Anthropic (optional) | the same channel evidence, for a second opinion | only `escalate --execute` |
165
+
166
+ Nothing else. No telemetry, no server, no account with us.
167
+
168
+ ## Cost
169
+
170
+ Jev costs $0.042 per million input tokens and output is free. An email is about 830 tokens, so sorting 12,158 emails
171
+ cost $0.43, and a year of daily runs costs under $1. Rerunning on mail it has already seen costs nothing (judgments
172
+ are cached). The same job on a frontier model would be roughly 180 to 300 times more:
173
+ [the numbers](https://github.com/abhibansal60/tidy/blob/main/docs/research/opus-5-5-plus-jev-cost.md).
174
+
175
+ ## Commands
176
+
177
+ | Command | What it does |
178
+ |---|---|
179
+ | `init`, `doctor` | first-run setup; check what is missing (offline) |
180
+ | `mail-auth [--write]` | Gmail sign-in (read, or label changes) |
181
+ | `mail-triage` | classify inbox mail; `--apply` auto-archives bulk mail |
182
+ | `mail-act` | apply reviewed Trash/Spam proposals from run files |
183
+ | `auth [--write]`, `sync` | YouTube sign-in; fetch subscriptions |
184
+ | `collect`, `judge`, `escalate` | gather evidence; Jev judges; optional second opinion |
185
+ | `propose`, `label`, `gate` | review page; record your verdicts; check the automation gate |
186
+ | `approve`, `unsubscribe`, `act`, `resubscribe` | change YouTube subscriptions (dry run until `--execute`) |
187
+ | `discover` | channels you watch a lot but do not follow |
188
+ | `report`, `purge` | counts; delete data past the 30-day retention |
189
+
190
+ `tidy COMMAND --help` for options. Full reference: [docs/reference.md](https://github.com/abhibansal60/tidy/blob/main/docs/reference.md).
191
+
192
+ ## FAQ
193
+
194
+ **Why not just ask ChatGPT or Claude to clean my inbox?** You can, but by our estimate you would pay 180 to 300 times more per email,
195
+ wait longer, and trust one model's word with your account. Tidy uses AI only for the judgment and keeps every rule
196
+ that touches your account in small, tested code.
197
+
198
+ **Will it delete an important email?** It cannot permanently delete anything. It never touches starred mail, only
199
+ archives on its own when two independent signals agree, and Trash is recoverable for 30 days.
200
+
201
+ **Is my data used to train anything?** Tidy has no server. What goes to TypeSafe is listed above; see their terms.
202
+
203
+ **Where is my data?** `~/.tidy` (or `./.tidy` inside a cloned checkout, or `$TIDY_DATA_DIR`). Delete the folder to
204
+ remove everything; revoke access at [myaccount.google.com/permissions](https://myaccount.google.com/permissions).
205
+
206
+ ## More
207
+
208
+ [Jev playbook](https://github.com/abhibansal60/tidy/blob/main/docs/guide/jev-playbook.md) ·
209
+ [Design decisions](https://github.com/abhibansal60/tidy/tree/main/docs/adr) ·
210
+ [Research and evals](https://github.com/abhibansal60/tidy/tree/main/docs/research) ·
211
+ [Changelog](https://github.com/abhibansal60/tidy/blob/main/CHANGELOG.md) ·
212
+ [Contributing](https://github.com/abhibansal60/tidy/blob/main/CONTRIBUTING.md)
213
+
214
+ Not affiliated with Google, YouTube or TypeSafe. MIT licensed. Use at your own risk.
@@ -0,0 +1,181 @@
1
+ # Tidy
2
+
3
+ **Clean up your Gmail inbox and your YouTube subscriptions with AI, without letting AI loose on your account.**
4
+
5
+ [Jev](https://docs.typesafe.ai) reads each email or channel and gives a typed verdict in well under a second. Plain,
6
+ tested code decides what that verdict is allowed to do, and nothing risky happens until you say so.
7
+
8
+ [![CI](https://github.com/abhibansal60/tidy/actions/workflows/ci.yml/badge.svg)](https://github.com/abhibansal60/tidy/actions/workflows/ci.yml)
9
+ [![PyPI](https://img.shields.io/pypi/v/tidy-ai)](https://pypi.org/project/tidy-ai/)
10
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://pypi.org/project/tidy-ai/)
11
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/abhibansal60/tidy/blob/main/LICENSE)
12
+
13
+ <img src="https://raw.githubusercontent.com/abhibansal60/tidy/main/docs/assets/mail_thumbnail.png" alt="Before: 12,158 unread emails. After: sorted, for 45 cents" width="640">
14
+
15
+ | | Gmail | YouTube |
16
+ |---|---|---|
17
+ | **Sorts** | every email into Needs Reply, Updates, Promos, Sales or Spam | every channel by relevance, value and how likely you are to watch it |
18
+ | **Does on its own** | archives bulk mail, only when a second signal agrees | nothing: every change waits for you |
19
+ | **Waits for you** | Trash and Spam, reviewed from a dashboard | unsubscribes and new subscriptions |
20
+ | **Never does** | permanently delete anything, touch starred mail, click unsubscribe links | use browser automation or scrape |
21
+ | **Measured cost** | $0.43 to sort 12,158 unread emails | a fraction of a cent for 110 channels |
22
+
23
+ [Mail demo video (MP4)](https://github.com/abhibansal60/tidy/raw/main/docs/assets/tidy_mail_demo.mp4) ·
24
+ [YouTube demo video (MP4)](https://github.com/abhibansal60/tidy/raw/main/docs/assets/tidy_demo.mp4) ·
25
+ [Cost write-up](https://github.com/abhibansal60/tidy/blob/main/docs/research/opus-5-5-plus-jev-cost.md)
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pipx install tidy-ai # or: uv tool install tidy-ai (or: pip install tidy-ai)
31
+ tidy --version
32
+ ```
33
+
34
+ Python 3.11 or newer. The command is `tidy`; `tidy-ai` is the same command, in case another `tidy` (HTML Tidy, which
35
+ macOS ships) comes first on your PATH. Until the first PyPI release: `pipx install git+https://github.com/abhibansal60/tidy`.
36
+
37
+ ## Set up once (about 10 minutes)
38
+
39
+ You need two things that only you can create:
40
+
41
+ 1. **A Google OAuth client.** In [Google Cloud Console](https://console.cloud.google.com/): create a project, enable the
42
+ **Gmail API** (and/or **YouTube Data API v3**), set up the OAuth consent screen in Testing mode with yourself as a
43
+ test user, then create an OAuth client of type **Desktop app** and download its JSON.
44
+ Step-by-step: [docs/agent-setup.md](https://github.com/abhibansal60/tidy/blob/main/docs/agent-setup.md#steps).
45
+ 2. **A TypeSafe API key** for Jev, from [console.typesafe.ai](https://console.typesafe.ai).
46
+
47
+ Then:
48
+
49
+ ```bash
50
+ tidy init --email you@gmail.com --client-secrets ~/Downloads/client_secret_XXXX.json --key-stdin
51
+ # paste the TypeSafe key when asked (it is hidden and stored 0600 in ~/.tidy/.env)
52
+ tidy doctor # checks everything, prints the next command, never prints a secret
53
+ ```
54
+
55
+ ## Clean up Gmail
56
+
57
+ ```bash
58
+ tidy mail-auth # read-only sign-in (a browser opens)
59
+ tidy mail-triage --execute --limit 200 --html inbox.html --json run.json
60
+ ```
61
+
62
+ Open `inbox.html`: every email with its category, why, and what Tidy proposes, plus an **unsubscribe shortlist**
63
+ grouped by sender (links you click yourself). Nothing in Gmail changed yet. When you like what you see:
64
+
65
+ ```bash
66
+ tidy mail-auth --write # a second, separate token that can change labels
67
+ tidy mail-triage --execute --apply --override-gate --limit 200 --json run.json # auto-archives bulk mail only
68
+ tidy mail-act --run run.json # preview the Trash/Spam proposals
69
+ tidy mail-act --run run.json --execute # apply them (Trash is recoverable for 30 days)
70
+ ```
71
+
72
+ `--override-gate` is needed because Tidy cannot yet prove from your own labels that its archiving matches your taste
73
+ ([why](https://github.com/abhibansal60/tidy/blob/main/docs/adr/0005-gated-owner-started-autonomy.md)). Keep it daily with cron:
74
+
75
+ ```cron
76
+ # use the full path from `command -v tidy`; cron has a minimal PATH
77
+ 0 8 * * * /home/you/.local/bin/tidy mail-triage --execute --apply --override-gate --query "in:inbox newer_than:2d" --json ~/.tidy/runs/$(date +\%F).json
78
+ ```
79
+
80
+ ## Clean up YouTube
81
+
82
+ ```bash
83
+ tidy auth # read-only sign-in
84
+ tidy sync # fetch your subscriptions
85
+ tidy collect --all --max-units 400 --execute # recent uploads per channel (YouTube quota: about 300 units)
86
+ tidy judge --schemas titles-desc-v1 --execute # Jev judges every channel
87
+ tidy propose --html review.html # open it: proposals with reasons and the commands to act
88
+ ```
89
+
90
+ <img src="https://raw.githubusercontent.com/abhibansal60/tidy/main/docs/assets/review_page.png" alt="Review page, synthetic data" width="640">
91
+
92
+ To act: `tidy auth --write`, `tidy approve CHANNEL_ID... --note "why"`, `tidy unsubscribe` (dry run), then
93
+ `tidy unsubscribe --execute`. A Google Takeout watch history makes proposals much better: see
94
+ [docs/reference.md](https://github.com/abhibansal60/tidy/blob/main/docs/reference.md).
95
+
96
+ ## Let an agent set it up
97
+
98
+ Paste into Claude Code, Codex or any coding agent:
99
+
100
+ ```text
101
+ Set up Tidy (pipx package tidy-ai) for me. Follow https://github.com/abhibansal60/tidy/blob/main/docs/agent-setup.md
102
+ exactly. Stop and ask me whenever a step needs my Google account, an API key or a browser. Never print my secrets,
103
+ and never run a command with --execute until I say so.
104
+ ```
105
+
106
+ Built for agents: every command prints JSON, `tidy doctor` says exactly what is missing and what to run next,
107
+ every change is a dry run until `--execute`, and secrets go in through a hidden prompt or stdin, never as arguments.
108
+
109
+ ## Safety
110
+
111
+ - **Dry run by default.** Nothing changes your account without `--execute`.
112
+ - **Two tokens.** Reading and changing use separate OAuth tokens; the change token is only requested when you ask.
113
+ - **Gmail cannot be permanently deleted by Tidy.** It never requests the full-access Gmail scope, only `gmail.modify`,
114
+ which can archive, trash and mark spam but cannot erase mail. Trash empties itself after 30 days, as Gmail always does.
115
+ - **AI is never enough on its own.** Auto-archive needs a second, code-owned signal (an unsubscribe header, not being
116
+ addressed to you personally, or Gmail's own Updates/Promotions tab). Starred mail is never proposed for anything.
117
+ - **Rechecked live.** Before any Trash, Spam or unsubscribe, Tidy re-reads the live account and skips anything you have
118
+ moved, starred or changed since.
119
+ - **Capped and budgeted.** Per-run caps, API call budgets checked before the first change, and an audit log.
120
+ - **Private files stay private.** Tokens, config, database, dashboards and run files are written owner-only (0600)
121
+ in `~/.tidy`; `tidy doctor` warns about anything readable by others.
122
+
123
+ Found a problem? See [SECURITY.md](https://github.com/abhibansal60/tidy/blob/main/SECURITY.md).
124
+
125
+ ## What leaves your machine
126
+
127
+ | Sent to | What | When |
128
+ |---|---|---|
129
+ | Google | the API calls you run | always, over HTTPS |
130
+ | TypeSafe (Jev) | per email: subject, sender, first 1,000 characters of text, two yes/no facts; per channel: its name and description, recent video titles, descriptions and lengths, and the interests (and optional viewing habits) you set in your profile | `mail-triage --execute`, `judge --execute` |
131
+ | Anthropic (optional) | the same channel evidence, for a second opinion | only `escalate --execute` |
132
+
133
+ Nothing else. No telemetry, no server, no account with us.
134
+
135
+ ## Cost
136
+
137
+ Jev costs $0.042 per million input tokens and output is free. An email is about 830 tokens, so sorting 12,158 emails
138
+ cost $0.43, and a year of daily runs costs under $1. Rerunning on mail it has already seen costs nothing (judgments
139
+ are cached). The same job on a frontier model would be roughly 180 to 300 times more:
140
+ [the numbers](https://github.com/abhibansal60/tidy/blob/main/docs/research/opus-5-5-plus-jev-cost.md).
141
+
142
+ ## Commands
143
+
144
+ | Command | What it does |
145
+ |---|---|
146
+ | `init`, `doctor` | first-run setup; check what is missing (offline) |
147
+ | `mail-auth [--write]` | Gmail sign-in (read, or label changes) |
148
+ | `mail-triage` | classify inbox mail; `--apply` auto-archives bulk mail |
149
+ | `mail-act` | apply reviewed Trash/Spam proposals from run files |
150
+ | `auth [--write]`, `sync` | YouTube sign-in; fetch subscriptions |
151
+ | `collect`, `judge`, `escalate` | gather evidence; Jev judges; optional second opinion |
152
+ | `propose`, `label`, `gate` | review page; record your verdicts; check the automation gate |
153
+ | `approve`, `unsubscribe`, `act`, `resubscribe` | change YouTube subscriptions (dry run until `--execute`) |
154
+ | `discover` | channels you watch a lot but do not follow |
155
+ | `report`, `purge` | counts; delete data past the 30-day retention |
156
+
157
+ `tidy COMMAND --help` for options. Full reference: [docs/reference.md](https://github.com/abhibansal60/tidy/blob/main/docs/reference.md).
158
+
159
+ ## FAQ
160
+
161
+ **Why not just ask ChatGPT or Claude to clean my inbox?** You can, but by our estimate you would pay 180 to 300 times more per email,
162
+ wait longer, and trust one model's word with your account. Tidy uses AI only for the judgment and keeps every rule
163
+ that touches your account in small, tested code.
164
+
165
+ **Will it delete an important email?** It cannot permanently delete anything. It never touches starred mail, only
166
+ archives on its own when two independent signals agree, and Trash is recoverable for 30 days.
167
+
168
+ **Is my data used to train anything?** Tidy has no server. What goes to TypeSafe is listed above; see their terms.
169
+
170
+ **Where is my data?** `~/.tidy` (or `./.tidy` inside a cloned checkout, or `$TIDY_DATA_DIR`). Delete the folder to
171
+ remove everything; revoke access at [myaccount.google.com/permissions](https://myaccount.google.com/permissions).
172
+
173
+ ## More
174
+
175
+ [Jev playbook](https://github.com/abhibansal60/tidy/blob/main/docs/guide/jev-playbook.md) ·
176
+ [Design decisions](https://github.com/abhibansal60/tidy/tree/main/docs/adr) ·
177
+ [Research and evals](https://github.com/abhibansal60/tidy/tree/main/docs/research) ·
178
+ [Changelog](https://github.com/abhibansal60/tidy/blob/main/CHANGELOG.md) ·
179
+ [Contributing](https://github.com/abhibansal60/tidy/blob/main/CONTRIBUTING.md)
180
+
181
+ Not affiliated with Google, YouTube or TypeSafe. MIT licensed. Use at your own risk.
@@ -0,0 +1,51 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tidy-ai"
7
+ dynamic = ["version"]
8
+ description = "Clean up your YouTube subscriptions and Gmail inbox with Jev: an AI judges, plain code sets the limits, you approve."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [{ name = "Abhinav Bansal" }]
13
+ requires-python = ">=3.11"
14
+ keywords = ["gmail", "inbox-zero", "email-cleanup", "youtube-subscriptions", "jev", "typesafe", "ai-agents", "cli"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Environment :: Console",
18
+ "Intended Audience :: End Users/Desktop",
19
+ "Intended Audience :: Developers",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Communications :: Email",
27
+ "Topic :: Utilities",
28
+ ]
29
+ dependencies = [
30
+ "google-auth>=2.40,<3",
31
+ "google-auth-oauthlib>=1.2,<2",
32
+ "requests>=2.32.4,<3", # CVE-2024-35195, CVE-2024-47081
33
+ "typesafe-sdk>=0.7,<1",
34
+ ]
35
+
36
+ [project.urls]
37
+ Homepage = "https://github.com/abhibansal60/tidy"
38
+ Documentation = "https://github.com/abhibansal60/tidy#readme"
39
+ Issues = "https://github.com/abhibansal60/tidy/issues"
40
+ Security = "https://github.com/abhibansal60/tidy/blob/main/SECURITY.md"
41
+ Changelog = "https://github.com/abhibansal60/tidy/blob/main/CHANGELOG.md"
42
+
43
+ [project.scripts]
44
+ tidy = "tidy.__main__:main"
45
+ tidy-ai = "tidy.__main__:main"
46
+
47
+ [tool.setuptools]
48
+ packages = ["tidy"]
49
+
50
+ [tool.setuptools.dynamic]
51
+ version = { attr = "tidy.__version__" }
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+