clauddy 1.29.0 โ 2.0.0
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.
- package/README.md +40 -339
- package/bin/clauddy.js +82 -79
- package/package.json +10 -82
- package/accounts.js +0 -181
- package/auth.js +0 -300
- package/codex.js +0 -236
- package/config.json +0 -11
- package/cursor.js +0 -278
- package/main.js +0 -1305
- package/preload.js +0 -43
- package/reminders.js +0 -54
- package/renderer/burn.js +0 -98
- package/renderer/companion.js +0 -52
- package/renderer/index.html +0 -527
- package/renderer/motion.css +0 -646
- package/renderer/pet.js +0 -2394
- package/renderer/pets.css +0 -916
- package/renderer/pets.js +0 -579
- package/renderer/style.css +0 -3758
- package/renderer/voice.js +0 -244
- package/usage.js +0 -539
package/README.md
CHANGED
|
@@ -1,378 +1,79 @@
|
|
|
1
1
|
# ๐ซ Clauddy
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A pixel-art desktop pet that keeps an eye on your Claude Code usage: the real session and weekly % from your account, the tokens from your local logs, and a pet that works, sleeps and catches fire along with it. It can follow Codex and Cursor too, and you can dress it as a wizard, Santa or a gingerbread cookie.
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
<
|
|
6
|
+
<a href="#running">๐ Get started</a> ยท
|
|
7
|
+
<a href="docs/design/02-features.md">โจ Features</a> ยท
|
|
8
|
+
<a href="docs/design/01-architecture.md">๐๏ธ Architecture</a> ยท
|
|
9
|
+
<a href="docs/design/03-setup.md">โ๏ธ Setup</a> ยท
|
|
10
|
+
<a href="docs/decisions/">๐งญ Decisions</a>
|
|
10
11
|
</p>
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- **Current session** โ real % used + **"resets in Xh Ym"** + session tokens, and a projection of where that pace is taking you (see [Burn rate](#burn-rate))
|
|
15
|
-
- **Weekly ยท all models** โ real % used + tokens over the last 7 days
|
|
16
|
-
- **Status line** under the pet: `โ working ยท 1.6M tok/min` (or today's tokens when idle)
|
|
17
|
-
- **By model ยท 7 days** โ Opus / Sonnet / Haiku / Fable, in tokens
|
|
18
|
-
- **By project ยท 7 days** โ which repo actually ate the week, ranked, with the tail folded into `other`
|
|
19
|
-
- **30-day map** โ colored squares by daily tokens (green = light โ red = heavy), with the monthly total
|
|
20
|
-
|
|
21
|
-
The **percentages are real**, pulled from your account (you log in once โ see below). The token counts, the by-model and by-project breakdowns, activity status, and 30-day map come from your local logs (`~/.claude/projects/**/*.jsonl`). Everything is token-based โ no dollars.
|
|
22
|
-
|
|
23
|
-
## Account & live usage
|
|
24
|
-
|
|
25
|
-
The session/weekly **%** comes straight from your Anthropic account, so it matches the official panel exactly. You connect once via a browser login:
|
|
26
|
-
|
|
27
|
-
1. Open **โ Settings โ Accounts โ Claude โ Connect** โ your browser opens an Anthropic auth page.
|
|
28
|
-
2. Log in, copy the **authentication code** shown, and paste it back into the app โ **Connect**.
|
|
29
|
-
|
|
30
|
-
The token is saved locally (see [Data & privacy](#data--privacy)) and refreshed automatically. **Until you connect**, the limits area shows a _"Connect your account"_ prompt instead of percentages.
|
|
31
|
-
|
|
32
|
-
### Several subscriptions
|
|
33
|
-
|
|
34
|
-
Got more than one Claude account โ say a personal Pro and a Max from work? The **account chip** in the top-left corner is the switcher: click it for the list of accounts, with the active one marked, plus **"+ Add another account"** โ which opens the same browser login and drops the token it brings back into a new slot. The tray icon has the same list under its **Account** submenu. Give up halfway and the empty slot disappears on its own โ the list only ever holds accounts you actually logged into.
|
|
35
|
-
|
|
36
|
-
The widget follows **one account at a time**: the one you pick is the one whose % is shown, whose logs are counted, and the only one that can notify you. Each account keeps its own token and its own armed alerts, so switching never replays a notification you already dismissed elsewhere. Everything else โ window position, display mode, zoom, thresholds โ is shared.
|
|
37
|
-
|
|
38
|
-
Removing an account (the **ร** on its row) deletes its token from disk. The one you're currently on can't be removed โ switch away first โ and neither can the last one left. Removing the first account clears its token without touching the settings that live in the same folder.
|
|
39
|
-
|
|
40
|
-
> Prefer one widget per account instead? Setting `CLAUDE_CONFIG_DIR` still isolates a whole instance โ token, settings and logs โ so you can run two Clauddys side by side.
|
|
41
|
-
|
|
42
|
-
## Codex
|
|
43
|
-
|
|
44
|
-
Clauddy can follow [Codex](https://openai.com/codex) (the CLI and the desktop app) next to Claude. Open **โ Settings โ Accounts โ Codex โ Connect**: it looks for a Codex session on this computer and starts monitoring if it finds one. There's nothing to log into โ Codex writes its token counts and rate limits to `~/.codex/sessions` (or `$CODEX_HOME`), and Clauddy only reads those files.
|
|
45
|
-
|
|
46
|
-
With both connected, **tabs under the pet** pick which service the whole panel shows โ account chip, pet mood, session and weekly %, the breakdowns, the 30-day map and the **โ** Usage link. The other tab keeps its % in view and gets a **!** when it crosses an alert threshold. Minimized, the ring becomes one reading per service; click one to switch.
|
|
47
|
-
|
|
48
|
-
A few things differ from Claude:
|
|
49
|
-
|
|
50
|
-
- **The % updates when Codex runs.** Its logs are only written during a session, so an idle Codex shows the last reading with its age (`read 1h 30m ago`). A window that has already reset since then shows **โ** rather than a made-up 0%.
|
|
51
|
-
- **It can lag the account page.** Codex limits are shared with other ChatGPT products (Work, Agents, Excel) that don't write to these logs.
|
|
52
|
-
- **No burn-rate projection and no activity scenes** โ the logs don't carry enough to draw them honestly.
|
|
53
|
-
|
|
54
|
-
Disconnecting (the **ร** on the Codex card) only stops monitoring in Clauddy; your Codex login and logs are untouched.
|
|
55
|
-
|
|
56
|
-
## Cursor
|
|
57
|
-
|
|
58
|
-
Clauddy can follow [Cursor](https://cursor.com) too. Open **โ Settings โ Accounts โ Cursor โ Connect**: it needs the Cursor app signed in on this computer, and starts monitoring once it finds that login. Clauddy reads the app's session token from its local storage and asks Cursor's own dashboard endpoints for your usage, the same numbers as the Usage page on cursor.com.
|
|
59
|
-
|
|
60
|
-
With three services connected, the tabs under the pet show each logo and %, and the minimized dock gets a third line.
|
|
61
|
-
|
|
62
|
-
Cursor works differently from the other two:
|
|
63
|
-
|
|
64
|
-
- **Its budget is monthly.** The Cursor tab shows **this month ยท included** (the whole plan) and **API models** (the pool for named third-party models), both resetting on your billing date. There is no 5-hour session or weekly window.
|
|
65
|
-
- **Activity scenes work.** Cursor's agent writes a transcript of every tool call under `~/.cursor/projects`, so the pet reads, edits, runs commands and researches along with it, as it does for Claude.
|
|
66
|
-
- **No token counts.** Neither source reports tokens, so the Cursor tab has no breakdowns or 30-day map, and its status line says when the agent last ran instead.
|
|
67
|
-
- **The endpoints are unofficial.** They're the ones the cursor.com dashboard uses, checked every 5 minutes. If Cursor changes them, the tab shows the last reading with its age until Clauddy is updated.
|
|
68
|
-
|
|
69
|
-
Disconnecting (the **ร** on the Cursor card) only stops monitoring in Clauddy; the Cursor app and its login are untouched.
|
|
70
|
-
|
|
71
|
-
## Burn rate
|
|
72
|
-
|
|
73
|
-
Knowing you're at **82%** with **1h 12m** left on the window still leaves you doing arithmetic in your head. So Clauddy does it for you: it fits the slope of your recent usage and projects when you'd hit 100% โ showing one extra line under the session bar:
|
|
74
|
-
|
|
75
|
-
- **`~35m left at this pace`** (in coral) โ you'd run out before the window resets. Ease off, or wrap up.
|
|
76
|
-
- **`resets before you run out`** โ the reset gets there first. Carry on.
|
|
77
|
-
|
|
78
|
-
The slope is fitted over your **session tokens** rather than the account %. The % is the number you care about, but it arrives as a whole number every few minutes โ over a short window the whole signal is a single `16 โ 17` step, which throws the fitted pace off by multiples. Local-log tokens step too โ one jump per assistant turn โ but in increments some 10โ20ร finer, so the slope is far steadier; the account % then anchors it, converting tokens into % and re-calibrating on every poll.
|
|
79
|
-
|
|
80
|
-
It reads your **recent** pace, not the session average: go quiet for a few minutes and the projection eases off, which is the point.
|
|
81
|
-
|
|
82
|
-
It only appears once there's enough to say honestly โ roughly 5 minutes into a session โ and stays hidden while you're idle, when the pace is flat, or right after a reset. A projection is a projection: change your pace and it changes with you.
|
|
83
|
-
|
|
84
|
-
## The pet's states
|
|
85
|
-
|
|
86
|
-
<table>
|
|
87
|
-
<tr>
|
|
88
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/idle.gif" width="280" alt="idle" /><br /><b>idle</b><br /><sub>breathes & blinks</sub></td>
|
|
89
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/working.gif" width="280" alt="working" /><br /><b>working</b><br /><sub>hops & eats token coins</sub></td>
|
|
90
|
-
</tr>
|
|
91
|
-
<tr>
|
|
92
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/on-fire.gif" width="280" alt="on fire" /><br /><b>on fire</b><br /><sub>session โฅ 90% โ red, shivers, flames</sub></td>
|
|
93
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/tired.gif" width="280" alt="maxed out" /><br /><b>maxed out</b><br /><sub>session at 100% โ drained, slumped, sweating</sub></td>
|
|
94
|
-
</tr>
|
|
95
|
-
<tr>
|
|
96
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/sleeping.gif" width="280" alt="sleeping" /><br /><b>sleeping</b><br /><sub>idle 5+ min โ blue zzz & moonlight</sub></td>
|
|
97
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/poke.gif" width="280" alt="poke" /><br /><b>poke</b><br /><sub>click the pet โ squish & hearts</sub></td>
|
|
98
|
-
</tr>
|
|
99
|
-
<tr>
|
|
100
|
-
<td colspan="2" align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/celebrate.gif" width="280" alt="celebrate" /><br /><b>celebrate</b><br /><sub>session resets โ jump & confetti</sub></td>
|
|
101
|
-
</tr>
|
|
102
|
-
</table>
|
|
103
|
-
|
|
104
|
-
Plus a welcome **wave** on launch. You can [poke the pet from the terminal](#play-with-the-pet) too.
|
|
105
|
-
|
|
106
|
-
### Pick your pet
|
|
107
|
-
|
|
108
|
-
Clauddy is the default, but **โ Settings โ Appearance โ pet** has more: a Wizard, a Halloween pack (Pumpkin, Skull, Ghost, Bat, Black cat) and a Christmas pack (Snowman, Santa, Reindeer, Gingerbread). Click one to preview it and **Save** to keep it. Each pet stays still and blinks when nothing is running, and has its own way of moving while a provider works: the Pumpkin rocks, the Bat flaps, the Wizard levitates while an owl flies by.
|
|
109
|
-
|
|
110
|
-
On holidays a pet from the season's pack takes over for the day, whichever pet you picked: one Halloween pet on October 31, one Christmas pet on December 24 and 25. The pick changes every year and holds all day. Your own pet comes back the next day.
|
|
111
|
-
|
|
112
|
-
### What Claude's up to
|
|
113
|
-
|
|
114
|
-
While Claude Code is actively working, the pet sets up a little desk scene that
|
|
115
|
-
mirrors **what it's doing right now** โ inferred from your local logs (the last
|
|
116
|
-
tool it used). The status line names the activity, and three of them get their
|
|
117
|
-
own animated scene:
|
|
118
|
-
|
|
119
|
-
<table>
|
|
120
|
-
<tr>
|
|
121
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/reading.gif" width="280" alt="reading" /><br /><b>reading</b><br /><sub>an open book under the reading lamp</sub></td>
|
|
122
|
-
<td align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/editing.gif" width="280" alt="editing" /><br /><b>editing</b><br /><sub>types at the desk, coffee in reach</sub></td>
|
|
123
|
-
</tr>
|
|
124
|
-
<tr>
|
|
125
|
-
<td colspan="2" align="center"><img src="https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/a8491cea34c4a8547965eadc3b24618ba50218e1/docs/media/running.gif" width="280" alt="running" /><br /><b>running</b><br /><sub>watches a task log tick through its checks</sub></td>
|
|
126
|
-
</tr>
|
|
127
|
-
</table>
|
|
128
|
-
|
|
129
|
-
Other activities โ **planning**, **researching**, **delegating**, **waiting** โ
|
|
130
|
-
show up in the status line as they happen. When Claude goes quiet, the pet drops
|
|
131
|
-
back to plain **working** / **idle**.
|
|
132
|
-
|
|
133
|
-
## The pet talks
|
|
134
|
-
|
|
135
|
-
Now and then the pet says something in a speech bubble. It only speaks when something changes, never on a timer, and it keeps quiet when it has nothing specific to say. Remarks are at least 10 minutes apart; the big moments can cut that line.
|
|
136
|
-
|
|
137
|
-
| Moment | What it says (expanded) | Minimized | Blip |
|
|
138
|
-
| --- | --- | --- | --- |
|
|
139
|
-
| First launch of the day | _Good morning! Yesterday was your heaviest day this week, 12M tokens._ | _Morning! Big day yesterday._ | โ |
|
|
140
|
-
| Session crosses your fire threshold | _Getting warm, 91% already. At this pace you'll run out in about 40m._ | _91%, getting warm!_ | โ |
|
|
141
|
-
| The session window resets | _Fresh window! That was 34M tokens over 4h 12m, mostly editing code._ | _Fresh window! 34M last time._ | โ |
|
|
142
|
-
| The session hits 100% | _That's the limit. I'll be back at 2:35 PM._ | _Maxed out till 2:35 PM._ | |
|
|
143
|
-
| You're back after 2+ hours away | _Welcome back! You were gone 3h 12m. The session's at 12%._ | _Welcome back!_ | |
|
|
144
|
-
| 90 minutes of unbroken work | _You've been at it for 1h 35m straight. Stretch break?_ | _Stretch break?_ | |
|
|
145
|
-
| A new record day (30-day best) | _New record! 45M tokens today, your biggest day in a month._ | _New record: 45M!_ | |
|
|
146
|
-
| Codex crosses fire, hits 100%, or resets | _Codex is at 91% now. It resets in 1h 12m._ | _Codex at 91%!_ | โ (fire, reset) |
|
|
147
|
-
| Cursor crosses fire, hits 100%, or starts a new month | _Cursor is at 91% now. It resets in 6d 4h._ | _Cursor at 91%!_ | โ (fire, reset) |
|
|
148
|
-
|
|
149
|
-
The greeting follows your clock (_Good morning_, _Good afternoon_, _Good evening_, or _Still up?_ before 5 AM) and appears once a day, and so does the record. The reset recap covers only what the pet actually saw, so if you launch it mid-session it says less. Click the bubble to dismiss it.
|
|
150
|
-
|
|
151
|
-
### The voice
|
|
152
|
-
|
|
153
|
-
Optionally the pet also **blips** as it talks: short chiptune notes, one per letter, generated live with Web Audio (square waves, no sound files). It blips faster and higher when it's on fire, slower and lower when it's sleepy. Only the headline moments blip (the โ rows above), because a sound on every remark wears out fast.
|
|
154
|
-
|
|
155
|
-
Audio should never catch you off guard in a meeting:
|
|
156
|
-
|
|
157
|
-
- **Off by default.** Turn it on under **โ Settings โ Appearance โ Chiptune voice** (you'll hear a sample).
|
|
158
|
-
- **One-click mute.** While the voice is on, a ๐ button sits in the title bar. Click it to mute for an hour, and again to unmute.
|
|
159
|
-
- **Always silent** in menu-bar mode and while the pet is minimized.
|
|
13
|
+
https://github.com/user-attachments/assets/be8d339c-ac69-4980-96a7-8e286f8b080f
|
|
160
14
|
|
|
161
|
-
|
|
15
|
+
## Stack
|
|
162
16
|
|
|
163
|
-
|
|
17
|
+
A native app in Rust with egui, no browser engine: about 1โ2% CPU and a 20 MB app. The `clauddy` command on npm is a small launcher that downloads it.
|
|
164
18
|
|
|
165
|
-
|
|
19
|
+
More details, with every tool and version, in [`docs/design/01-architecture.md`](docs/design/01-architecture.md#stack).
|
|
166
20
|
|
|
167
|
-
|
|
21
|
+
## Requirements
|
|
168
22
|
|
|
169
|
-
|
|
23
|
+
- macOS on Apple Silicon (the first-class build), Windows x64 or Linux x64
|
|
24
|
+
- [Claude Code](https://claude.com/claude-code), whose logs in `~/.claude/projects` give the token counts; a Claude login, done once in Settings, gives the real %
|
|
25
|
+
- [Bun](https://bun.sh), or Node 18+ for `npx`, only to run it with `bunx clauddy`
|
|
26
|
+
- [Rust](https://rustup.rs), only to work on the code
|
|
27
|
+
- Optional: [Codex](https://openai.com/codex) or the [Cursor](https://cursor.com) app on the same machine, to follow them too
|
|
170
28
|
|
|
171
|
-
|
|
29
|
+
## Running
|
|
172
30
|
|
|
173
|
-
|
|
31
|
+
On macOS, as an app that opens at login:
|
|
174
32
|
|
|
175
33
|
```bash
|
|
176
34
|
curl -fsSL https://raw.githubusercontent.com/renatoaug/claude-usage-monitor/main/install.sh | bash
|
|
177
35
|
```
|
|
178
36
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Once installed, you can update in-app: **โ Settings โ Check for updates โ Update now** runs the same installer and relaunches the new build.
|
|
182
|
-
|
|
183
|
-
Why not a normal download? macOS blocks **unsigned** apps downloaded through a browser with a scary *"damaged, move to Trash"* warning โ even when they're perfectly safe. It's a false alarm: the only way to silence it is to pay Apple **$99/year** to sign + notarize, which a free hobby app skips. Files fetched with `curl` aren't flagged, so this method simply **lets your Mac open the app** without the block. It then registers in **Login Items** and starts with your Mac โ set it and forget it.
|
|
184
|
-
|
|
185
|
-
#### 2. Run it via `bunx` (no install)
|
|
186
|
-
|
|
187
|
-
Needs [Bun](https://bun.sh) (or use `npx` with Node 24):
|
|
37
|
+
Anywhere, without installing:
|
|
188
38
|
|
|
189
39
|
```bash
|
|
190
40
|
bunx clauddy
|
|
191
41
|
```
|
|
192
42
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
### Windows (x64)
|
|
196
|
-
|
|
197
|
-
The quickest path works the same as macOS โ with [Bun](https://bun.sh) or Node 24 installed:
|
|
198
|
-
|
|
199
|
-
```powershell
|
|
200
|
-
bunx clauddy # or: npx clauddy
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Prefer a standalone app with no Node/Bun? Grab the **portable zip** (`Clauddy-<version>-win-x64.zip`) from the [latest release](https://github.com/renatoaug/claude-usage-monitor/releases), unzip it anywhere, and run `Clauddy.exe`. Because the app is unsigned, Windows **SmartScreen** shows a "Windows protected your PC" prompt the first time โ click **More info โ Run anyway**. From then on it starts with Windows.
|
|
204
|
-
|
|
205
|
-
### Linux (x64)
|
|
206
|
-
|
|
207
|
-
The quickest path works the same as macOS โ with [Bun](https://bun.sh) or Node 24 installed:
|
|
208
|
-
|
|
209
|
-
```bash
|
|
210
|
-
bunx clauddy # or: npx clauddy
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
Prefer a standalone app? Grab the **AppImage** or **tar.gz** (`Clauddy-<version>-linux-x86_64.AppImage` / `Clauddy-<version>-linux-x64.tar.gz`) from the [latest release](https://github.com/renatoaug/claude-usage-monitor/releases), then:
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
chmod +x Clauddy-*.AppImage
|
|
217
|
-
./Clauddy-*.AppImage
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Want the pet icon to show up in your app menu / taskbar too, instead of a generic icon? Running the AppImage directly doesn't register it anywhere โ GNOME (and most Wayland desktops) only pick up an app's icon from an installed `.desktop` entry. One-time setup, right next to the AppImage:
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
./Clauddy-*.AppImage --appimage-extract clauddy.desktop >/dev/null
|
|
224
|
-
./Clauddy-*.AppImage --appimage-extract usr/share/icons/hicolor/512x512/apps/clauddy.png >/dev/null
|
|
225
|
-
mkdir -p ~/.local/share/applications ~/.local/share/icons/hicolor/512x512/apps
|
|
226
|
-
cp squashfs-root/usr/share/icons/hicolor/512x512/apps/clauddy.png ~/.local/share/icons/hicolor/512x512/apps/clauddy.png
|
|
227
|
-
sed "s|^Exec=.*|Exec=$(readlink -f Clauddy-*.AppImage) --no-sandbox %U|" squashfs-root/clauddy.desktop > ~/.local/share/applications/clauddy.desktop
|
|
228
|
-
rm -rf squashfs-root
|
|
229
|
-
update-desktop-database ~/.local/share/applications 2>/dev/null
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
> The system tray icon needs an indicator extension on vanilla GNOME (e.g. "AppIndicator and KStatusNotifier Item Support") โ it works out of the box on Cinnamon, KDE, and XFCE. Autostart-at-login is wired up via an XDG `.desktop` entry in `~/.config/autostart/`.
|
|
233
|
-
|
|
234
|
-
> The app keeps its data in `~/.claude-usage-monitor`, regardless of platform or how you run it.
|
|
235
|
-
|
|
236
|
-
## Controls
|
|
237
|
-
|
|
238
|
-
- **Drag** the widget anywhere on screen
|
|
239
|
-
- **โ** minimizes to just the pet, ringed by the live session % (the number sits inside the ring) โ or, with more than one service connected, one reading per service; the **โคข** button or a double-click on the pet expands it back
|
|
240
|
-
- **Pet icon** in the compact header switches to **Just the pet** (floating mode). Drag the pet itself to move it. Hovering over the pet (or focusing it) pops up the usage glance as the pet's own speech bubble, one reading and a usage bar per service; the small expand button in it returns to compact. Keyboard users can focus the pet and press Enter. The chosen size is remembered. In compact and pet-only modes, the pet follows whoever is working, with provider logos identifying Claude, Codex, Cursor, or several at once. Usage selection stays manual. When neither is active, the hottest session still sets the mood: the pet catches fire (or maxes out) with that provider's logo beside it, and otherwise rests.
|
|
241
|
-
- **โ** opens settings (log in, toggle alerts, set thresholds, pick the display mode)
|
|
242
|
-
- **โ** opens the official Usage page of the service on screen
|
|
243
|
-
- **ร** quits
|
|
244
|
-
|
|
245
|
-
### Floating or menu bar
|
|
246
|
-
|
|
247
|
-
Under **โ Settings โ Appearance** you can pick where Clauddy lives:
|
|
248
|
-
|
|
249
|
-
- **Floating pet** โ the always-on widget in the corner (default).
|
|
250
|
-
- **Menu bar** โ a small pet icon in the macOS menu bar showing your live session **%** (it turns ๐ฅ near your limit; with Codex or Cursor too it reads `C 64% ยท X 28% ยท Cu 5%`). Click it to pop open the full pet + usage panel; click away to dismiss. Right-click for a quick menu.
|
|
251
|
-
|
|
252
|
-
Switching is instant โ no restart. (On Windows/Linux the icon lives in the system tray; the live % shows in its tooltip.)
|
|
253
|
-
|
|
254
|
-
### Provider reactions
|
|
255
|
-
|
|
256
|
-
When a provider's activity changes, the pet briefly glances toward it, with no caption. Both can be working at once. A glance never changes your selected provider, and glances share a 30-second cooldown. Inactivity is treated as a pause, not a completed task.
|
|
257
|
-
|
|
258
|
-
## Alerts
|
|
259
|
-
|
|
260
|
-
Optional **macOS notifications**, toggled (with their thresholds) in **โ Settings โ Alerts**:
|
|
261
|
-
|
|
262
|
-
| Notification | When |
|
|
263
|
-
| --- | --- |
|
|
264
|
-
| _Session at 82%_ โ `2h 39m left ยท resets 6:50 PM` | Your session crosses a threshold (default **80%** and **95%**) |
|
|
265
|
-
| _Weekly usage at 84%_ โ `resets Fri 7:00 AM` | Same, for the weekly limit |
|
|
266
|
-
| _Fable weekly at 84%_ โ `resets Fri 7:00 AM` | Same, for a per-model weekly limit |
|
|
267
|
-
| _Codex session at 82%_ / _Codex weekly at 84%_ | Same, for Codex, when it's connected |
|
|
268
|
-
| _Cursor usage at 82%_ / _Cursor API models at 84%_ โ `resets Oct 11` | Same, for Cursor's month, when it's connected |
|
|
269
|
-
| _Session window reset_ โ `full budget again` | A session you had pushed past 80% rolls over |
|
|
270
|
-
| _Clauddy lost access to your usage_ | The OAuth token expired or was revoked, so the % went back to being an estimate |
|
|
271
|
-
|
|
272
|
-
The last threshold you set is the only one that makes a sound; the earlier ones arrive silently. Clicking any of them brings the widget to the front. Each fires once and re-arms when usage drops back below โ remembered across restarts, so relaunching at 85% doesn't repeat an alert you already dismissed.
|
|
273
|
-
|
|
274
|
-
Percentages come from your account when you're logged in, and fall back to the local token estimate when you're not.
|
|
275
|
-
|
|
276
|
-
The first alert asks macOS for permission; after that the app shows up in **System Settings > Notifications** like any other.
|
|
277
|
-
|
|
278
|
-
### Remind me at the session reset
|
|
279
|
-
|
|
280
|
-
When the selected session crosses your first alert threshold, **Notify at reset** appears beneath the session details and in the compact monitor. Click to arm it; click **Reminder on** to cancel. This is a one-shot request, independent of the automatic threshold-alert toggle.
|
|
281
|
-
|
|
282
|
-
Reminders survive restarts, belong to the Claude account, Codex or Cursor connection that created them, and are canceled when you disconnect that source. Clauddy must be running to notify; an overdue reminder is delivered on reopening or waking up. A fresh, lower reading confirms that session budget returned and the pet celebrates. If usage hasn't refreshed โ common with idle Codex logs โ the reminder only says the scheduled reset time arrived and asks you to check usage.
|
|
283
|
-
|
|
284
|
-
## Configure (`config.json`)
|
|
285
|
-
|
|
286
|
-
Settings saved from the UI live in `~/.claude-usage-monitor/config.json`, so you can tweak them without rebuilding:
|
|
287
|
-
|
|
288
|
-
```jsonc
|
|
289
|
-
{
|
|
290
|
-
"mode": "floating", // "floating" pet in the corner, or "menubar" popover
|
|
291
|
-
"alerts": true, // macOS notifications on/off
|
|
292
|
-
"alertThresholds": [80, 95], // notify when session/week cross these % (two levels)
|
|
293
|
-
"fireThreshold": 90, // session % at which the pet catches fire (maxed out stays 100)
|
|
294
|
-
"pollIntervalMs": 4000, // how often local logs are re-read
|
|
295
|
-
"activeThresholdMs": 20000, // fallback "active" window, for a log whose tail doesn't say whether a turn is open
|
|
296
|
-
"sleepThresholdMs": 300000, // "sleeping" after this much idle time (5 min)
|
|
297
|
-
"codex": { "enabled": false }, // set by Settings โ Codex โ Connect
|
|
298
|
-
"cursor": { "enabled": false }, // set by Settings โ Cursor โ Connect
|
|
299
|
-
"talk": true, // speech bubbles on transitions
|
|
300
|
-
"sound": false, // chiptune blips, opt-in
|
|
301
|
-
"soundMutedUntil": 0, // set by the ๐ button: muted until this epoch ms
|
|
302
|
-
}
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
## Play with the pet
|
|
43
|
+
[`docs/design/03-setup.md`](docs/design/03-setup.md) has Windows and Linux, updating, running from the code and every setting in `config.json`.
|
|
306
44
|
|
|
307
|
-
|
|
45
|
+
## Test
|
|
308
46
|
|
|
309
47
|
```bash
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
bunx clauddy tired # ๐ฅต maxed out
|
|
316
|
-
bunx clauddy idle # ๐ calm
|
|
317
|
-
bunx clauddy auto # โฉ๏ธ back to your real usage
|
|
318
|
-
bunx clauddy say # ๐ฌ a remark in the speech bubble
|
|
319
|
-
bunx clauddy say fire # or a specific one: greeting, fire, reset, maxed, welcome, streak, record, codex, cursor
|
|
48
|
+
cd native
|
|
49
|
+
cargo fmt --check # formatting
|
|
50
|
+
cargo test --release # unit tests and the snapshot gallery
|
|
51
|
+
scripts/bundle.sh # builds target/Clauddy.app (macOS)
|
|
52
|
+
CLAUDDY_STATE=fire cargo run --release # forces a mood: idle, working, fire, tired, sleeping
|
|
320
53
|
```
|
|
321
54
|
|
|
322
|
-
|
|
323
|
-
live. (Installed globally? Drop the `bunx`: `clauddy poke`. Working on the repo?
|
|
324
|
-
`./pet <state>` does the same.)
|
|
325
|
-
|
|
326
|
-
## How it works
|
|
327
|
-
|
|
328
|
-
- **`main.js`** โ Electron main process: frameless, transparent, always-on-top window; polls usage; fires macOS notifications; watches `config.json` and `debug.json`.
|
|
329
|
-
- **`usage.js`** โ reads `~/.claude/projects/**/*.jsonl`, sums tokens per model/project/day, detects the rolling 5-hour session window, the working/sleeping status, and which activity (reading/editing/running/โฆ) Claude is on from its latest tool use.
|
|
330
|
-
- **`auth.js`** โ OAuth login (PKCE, same public client as Claude Code) that fetches the authoritative usage %. Token stored locally, never committed.
|
|
331
|
-
- **`renderer/`** โ the pet itself: an SVG pixel sprite, CSS animations, and the Web Animations API for particles. `voice.js` writes its remarks and synthesizes the blips.
|
|
332
|
-
- **`make-icon.js`** โ generates the app icon from the pixel sprite (`build/icon.icns`).
|
|
333
|
-
|
|
334
|
-
## Data & privacy
|
|
335
|
-
|
|
336
|
-
Everything lives on your machine, in `~/.claude-usage-monitor/`:
|
|
55
|
+
The snapshot gallery renders every screen state into `native/tests/snapshots/`; after an intended visual change, `UPDATE_SNAPSHOTS=1 cargo test --release` rewrites it. At the root, `bun run test` tests the npm launcher, and `bun install` turns on a pre-commit hook that formats it. CI runs all of it on every pull request.
|
|
337
56
|
|
|
338
|
-
|
|
339
|
-
- `config.json` โ your alert settings
|
|
340
|
-
- `alerts.json` โ which notifications are already armed, so a restart doesn't repeat them
|
|
341
|
-
- `accounts.json` โ your list of accounts and which one is active
|
|
57
|
+
## Releases
|
|
342
58
|
|
|
343
|
-
|
|
344
|
-
- `debug.json` โ scratch file for the `./pet` simulator
|
|
345
|
-
- `accounts/<id>/` โ the same `auth.json` + `alerts.json`, for each extra account
|
|
59
|
+
Every merge to `main` can be a release: [semantic-release](https://semantic-release.gitbook.io) reads the Conventional Commit titles since the last tag (a `feat` bumps the minor, a `fix` or `perf` the patch, `docs` and `chore` nothing), builds the app on macOS, Windows and Linux runners, publishes the GitHub release and `clauddy` on npm. The whole pipeline is in [`docs/design/07-releases.md`](docs/design/07-releases.md).
|
|
346
60
|
|
|
347
|
-
|
|
61
|
+
## Docs
|
|
348
62
|
|
|
349
|
-
|
|
63
|
+
Everything about how Clauddy works, runs and was decided is indexed in [`docs/`](docs/README.md). Code rules and the checks before calling a change done are in [`AGENTS.md`](AGENTS.md).
|
|
350
64
|
|
|
351
|
-
|
|
352
|
-
for setup and the few gotchas worth knowing before a first PR.
|
|
65
|
+
We follow [Conventional Commits](https://www.conventionalcommits.org).
|
|
353
66
|
|
|
354
|
-
##
|
|
67
|
+
## Privacy
|
|
355
68
|
|
|
356
|
-
-
|
|
357
|
-
- **Tests**: `bun run test` (never bare `bun test` โ the groups under `test/`
|
|
358
|
-
must each run in their own process). `bun run test:coverage` enforces the
|
|
359
|
-
floor; every PR runs both.
|
|
360
|
-
- **Biome** for format + lint (`bun run check`); a versioned **pre-commit hook** (`.githooks/pre-commit`) auto-formats staged files and blocks on errors. It's wired up automatically on `bun install` (via the `prepare` script).
|
|
69
|
+
Everything stays on your machine, in `~/.claude-usage-monitor/`. Nothing leaves it except the OAuth and usage calls to Anthropic and, with Cursor connected, the usage calls to Cursor with the Cursor app's own token. What is kept and what is read: [`docs/design/01-architecture.md`](docs/design/01-architecture.md#data-on-disk).
|
|
361
70
|
|
|
362
|
-
|
|
71
|
+
## Community
|
|
363
72
|
|
|
364
|
-
|
|
365
|
-
[
|
|
366
|
-
|
|
367
|
-
computes the version, builds the app for **macOS, Windows and Linux** on their
|
|
368
|
-
own runners, publishes `clauddy` to npm, and cuts a GitHub Release with every
|
|
369
|
-
artifact attached. Nothing to do by hand โ just merge your PRs.
|
|
73
|
+
- ๐ [GitHub Issues](https://github.com/renatoaug/claude-usage-monitor/issues): report a bug or propose a feature
|
|
74
|
+
- ๐ง Pull requests are welcome: read [`CONTRIBUTING.md`](CONTRIBUTING.md) first for how to start and the checks
|
|
75
|
+
- ๐ [@renatoaug on GitHub](https://github.com/renatoaug): the maintainer
|
|
370
76
|
|
|
371
|
-
|
|
372
|
-
Windows/Linux from macOS: `version` (a semantic-release dry-run that computes
|
|
373
|
-
the next version) โ `build` (a matrix that stamps that version into
|
|
374
|
-
`package.json` so the filenames are right) โ `publish` (downloads every
|
|
375
|
-
artifact and runs semantic-release for real).
|
|
77
|
+
## License
|
|
376
78
|
|
|
377
|
-
|
|
378
|
-
- `docs:`/`chore:`/`ci:` etc. don't trigger a release.
|
|
79
|
+
Clauddy is licensed under the [MIT License](LICENSE).
|
package/bin/clauddy.js
CHANGED
|
@@ -1,87 +1,90 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// `clauddy`
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
|
|
3
|
+
// `clauddy` launches the native app (native/, Rust). The first run of each
|
|
4
|
+
// version downloads that version's release build for this OS into
|
|
5
|
+
// ~/.claude-usage-monitor/bin/<version>/; later runs open it straight away.
|
|
6
|
+
// The app runs on its own: this launcher exits as soon as it has started it.
|
|
7
7
|
const fs = require('node:fs')
|
|
8
8
|
const os = require('node:os')
|
|
9
|
+
const path = require('node:path')
|
|
10
|
+
const { spawn, execFileSync } = require('node:child_process')
|
|
11
|
+
|
|
12
|
+
const REPO = 'renatoaug/claude-usage-monitor'
|
|
13
|
+
|
|
14
|
+
// the release asset for an OS, and the file inside it to start
|
|
15
|
+
function assetFor(platform, arch, version) {
|
|
16
|
+
if (platform === 'darwin' && arch === 'arm64')
|
|
17
|
+
return { file: `Clauddy-${version}-arm64-mac.zip`, app: 'Clauddy.app' }
|
|
18
|
+
if (platform === 'win32' && arch === 'x64')
|
|
19
|
+
return { file: `Clauddy-${version}-win-x64.zip`, app: 'Clauddy.exe' }
|
|
20
|
+
if (platform === 'linux' && arch === 'x64')
|
|
21
|
+
return { file: `Clauddy-${version}-linux-x64.tar.gz`, app: path.join('Clauddy', 'clauddy') }
|
|
22
|
+
return undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function dataDir() {
|
|
26
|
+
return process.env.CLAUDE_CONFIG_DIR
|
|
27
|
+
? path.join(process.env.CLAUDE_CONFIG_DIR, 'usage-monitor')
|
|
28
|
+
: path.join(os.homedir(), '.claude-usage-monitor')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function install(asset, version, dir) {
|
|
32
|
+
const url = `https://github.com/${REPO}/releases/download/v${version}/${asset.file}`
|
|
33
|
+
console.log(`clauddy: downloading ${asset.file}โฆ`)
|
|
34
|
+
const res = await fetch(url)
|
|
35
|
+
if (!res.ok) throw new Error(`download failed (${res.status}): ${url}`)
|
|
36
|
+
const tmp = `${dir}.tmp`
|
|
37
|
+
fs.rmSync(tmp, { recursive: true, force: true })
|
|
38
|
+
fs.mkdirSync(tmp, { recursive: true })
|
|
39
|
+
const archive = path.join(tmp, asset.file)
|
|
40
|
+
fs.writeFileSync(archive, Buffer.from(await res.arrayBuffer()))
|
|
41
|
+
// ditto keeps the app bundle's signature; tar reads zips on Windows 10+
|
|
42
|
+
if (process.platform === 'darwin') execFileSync('ditto', ['-x', '-k', archive, tmp])
|
|
43
|
+
else execFileSync('tar', ['-xf', archive, '-C', tmp])
|
|
44
|
+
fs.rmSync(archive)
|
|
45
|
+
fs.rmSync(dir, { recursive: true, force: true })
|
|
46
|
+
fs.renameSync(tmp, dir)
|
|
47
|
+
}
|
|
9
48
|
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
49
|
+
// older versions' builds are dead weight once a newer one is in
|
|
50
|
+
function prune(root, keep) {
|
|
51
|
+
for (const name of fs.readdirSync(root)) {
|
|
52
|
+
if (name !== keep) fs.rmSync(path.join(root, name), { recursive: true, force: true })
|
|
53
|
+
}
|
|
54
|
+
}
|
|
14
55
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
'
|
|
18
|
-
|
|
19
|
-
'
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
'
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
|
|
41
|
-
'welcome',
|
|
42
|
-
'streak',
|
|
43
|
-
'record',
|
|
44
|
-
'codex',
|
|
45
|
-
'cursor',
|
|
46
|
-
]
|
|
47
|
-
const arg = process.argv[2]
|
|
48
|
-
const kind = arg === 'say' ? process.argv[3] || 'greeting' : undefined
|
|
56
|
+
async function main() {
|
|
57
|
+
const arg = process.argv[2]
|
|
58
|
+
if (arg === '--help' || arg === '-h') {
|
|
59
|
+
console.log(
|
|
60
|
+
'clauddy โ a desktop pet that tracks your Claude Code usage\n\nUsage:\n clauddy launch the app',
|
|
61
|
+
)
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
const { version } = require('../package.json')
|
|
65
|
+
const asset = assetFor(process.platform, process.arch, version)
|
|
66
|
+
if (!asset) {
|
|
67
|
+
console.error(
|
|
68
|
+
`clauddy: no build for ${process.platform}-${process.arch} (macOS arm64, Windows x64, Linux x64)`,
|
|
69
|
+
)
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
const root = path.join(dataDir(), 'bin')
|
|
73
|
+
const dir = path.join(root, version)
|
|
74
|
+
const app = path.join(dir, asset.app)
|
|
75
|
+
if (!fs.existsSync(app)) {
|
|
76
|
+
await install(asset, version, dir)
|
|
77
|
+
prune(root, version)
|
|
78
|
+
}
|
|
79
|
+
const [cmd, args] = process.platform === 'darwin' ? ['open', [app]] : [app, []]
|
|
80
|
+
spawn(cmd, args, { detached: true, stdio: 'ignore' }).unref()
|
|
81
|
+
}
|
|
49
82
|
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
child.on('close', (code) => process.exit(code ?? 0))
|
|
56
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
57
|
-
console.log(
|
|
58
|
-
[
|
|
59
|
-
'clauddy โ a cute desktop pet that tracks your Claude Code usage',
|
|
60
|
-
'',
|
|
61
|
-
'Usage:',
|
|
62
|
-
' clauddy launch the widget',
|
|
63
|
-
' clauddy <state> poke the running widget (just for fun)',
|
|
64
|
-
' clauddy say [kind] make the pet say one of its remarks',
|
|
65
|
-
'',
|
|
66
|
-
'States: fire, sleeping, working, tired, idle, poke, celebrate, auto',
|
|
67
|
-
`Remarks: ${REMARKS.join(', ')}`,
|
|
68
|
-
'(the widget must be running for a state to show)',
|
|
69
|
-
].join('\n'),
|
|
70
|
-
)
|
|
71
|
-
} else if (kind && !REMARKS.includes(kind)) {
|
|
72
|
-
console.error(`clauddy: unknown remark "${kind}"`)
|
|
73
|
-
console.error(`try: ${REMARKS.join(', ')}`)
|
|
74
|
-
process.exit(1)
|
|
75
|
-
} else if (STATES.includes(arg)) {
|
|
76
|
-
// write the state for the running app to pick up
|
|
77
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
78
|
-
fs.writeFileSync(
|
|
79
|
-
path.join(dataDir, 'debug.json'),
|
|
80
|
-
`${JSON.stringify({ state: arg, kind, t: Date.now() })}\n`,
|
|
81
|
-
)
|
|
82
|
-
console.log(`clauddy โ ${arg}${kind ? ` ${kind}` : ''} (the running widget will react)`)
|
|
83
|
-
} else {
|
|
84
|
-
console.error(`clauddy: unknown command "${arg}"`)
|
|
85
|
-
console.error('try: fire, sleeping, working, tired, idle, poke, celebrate, auto โ or --help')
|
|
86
|
-
process.exit(1)
|
|
83
|
+
if (require.main === module) {
|
|
84
|
+
main().catch((e) => {
|
|
85
|
+
console.error(`clauddy: ${e.message}`)
|
|
86
|
+
process.exit(1)
|
|
87
|
+
})
|
|
87
88
|
}
|
|
89
|
+
|
|
90
|
+
module.exports = { assetFor }
|