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.
- tidy_ai-0.2.0/LICENSE +21 -0
- tidy_ai-0.2.0/PKG-INFO +214 -0
- tidy_ai-0.2.0/README.md +181 -0
- tidy_ai-0.2.0/pyproject.toml +51 -0
- tidy_ai-0.2.0/setup.cfg +4 -0
- tidy_ai-0.2.0/tests/test_act.py +268 -0
- tidy_ai-0.2.0/tests/test_codex_baseline.py +83 -0
- tidy_ai-0.2.0/tests/test_collector.py +171 -0
- tidy_ai-0.2.0/tests/test_discovery.py +90 -0
- tidy_ai-0.2.0/tests/test_escalate.py +74 -0
- tidy_ai-0.2.0/tests/test_evidence_store.py +99 -0
- tidy_ai-0.2.0/tests/test_experiment.py +121 -0
- tidy_ai-0.2.0/tests/test_gmail.py +254 -0
- tidy_ai-0.2.0/tests/test_inventory.py +204 -0
- tidy_ai-0.2.0/tests/test_judge.py +194 -0
- tidy_ai-0.2.0/tests/test_label_retest.py +35 -0
- tidy_ai-0.2.0/tests/test_mail.py +363 -0
- tidy_ai-0.2.0/tests/test_mail_report_html.py +113 -0
- tidy_ai-0.2.0/tests/test_mutation.py +162 -0
- tidy_ai-0.2.0/tests/test_pilot.py +70 -0
- tidy_ai-0.2.0/tests/test_policy.py +120 -0
- tidy_ai-0.2.0/tests/test_profile.py +30 -0
- tidy_ai-0.2.0/tests/test_rename.py +41 -0
- tidy_ai-0.2.0/tests/test_report_html.py +134 -0
- tidy_ai-0.2.0/tests/test_review.py +222 -0
- tidy_ai-0.2.0/tests/test_setup_check.py +113 -0
- tidy_ai-0.2.0/tests/test_ship_fixes.py +100 -0
- tidy_ai-0.2.0/tests/test_watch_history.py +44 -0
- tidy_ai-0.2.0/tidy/__init__.py +3 -0
- tidy_ai-0.2.0/tidy/__main__.py +400 -0
- tidy_ai-0.2.0/tidy/collector.py +72 -0
- tidy_ai-0.2.0/tidy/discovery.py +28 -0
- tidy_ai-0.2.0/tidy/escalate.py +77 -0
- tidy_ai-0.2.0/tidy/experiment.py +72 -0
- tidy_ai-0.2.0/tidy/gmail.py +314 -0
- tidy_ai-0.2.0/tidy/judge.py +165 -0
- tidy_ai-0.2.0/tidy/mail.py +260 -0
- tidy_ai-0.2.0/tidy/mail_report_html.py +151 -0
- tidy_ai-0.2.0/tidy/mutate.py +251 -0
- tidy_ai-0.2.0/tidy/pilot.py +40 -0
- tidy_ai-0.2.0/tidy/policy.py +93 -0
- tidy_ai-0.2.0/tidy/profile.py +53 -0
- tidy_ai-0.2.0/tidy/proposal.py +14 -0
- tidy_ai-0.2.0/tidy/report_html.py +208 -0
- tidy_ai-0.2.0/tidy/review.py +145 -0
- tidy_ai-0.2.0/tidy/setup_check.py +137 -0
- tidy_ai-0.2.0/tidy/store.py +378 -0
- tidy_ai-0.2.0/tidy/watch_history.py +32 -0
- tidy_ai-0.2.0/tidy/youtube.py +222 -0
- tidy_ai-0.2.0/tidy_ai.egg-info/PKG-INFO +214 -0
- tidy_ai-0.2.0/tidy_ai.egg-info/SOURCES.txt +53 -0
- tidy_ai-0.2.0/tidy_ai.egg-info/dependency_links.txt +1 -0
- tidy_ai-0.2.0/tidy_ai.egg-info/entry_points.txt +3 -0
- tidy_ai-0.2.0/tidy_ai.egg-info/requires.txt +4 -0
- 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
|
+
[](https://github.com/abhibansal60/tidy/actions/workflows/ci.yml)
|
|
42
|
+
[](https://pypi.org/project/tidy-ai/)
|
|
43
|
+
[](https://pypi.org/project/tidy-ai/)
|
|
44
|
+
[](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.
|
tidy_ai-0.2.0/README.md
ADDED
|
@@ -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
|
+
[](https://github.com/abhibansal60/tidy/actions/workflows/ci.yml)
|
|
9
|
+
[](https://pypi.org/project/tidy-ai/)
|
|
10
|
+
[](https://pypi.org/project/tidy-ai/)
|
|
11
|
+
[](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__" }
|
tidy_ai-0.2.0/setup.cfg
ADDED