Great tool! I was looking for something similar to this.
I took a quick look at the code and had a small question/suggestion. It seems Playwright is only used to scrape pricing from cursor.com. Since pricing is available as a static Markdown file (https://cursor.com/docs/models-and-pricing.md), do you think Playwright could be removed as a dependency?
One thing I was also wondering about: have you run into any inconsistencies between the model names stored in Cursor’s local database and the ones listed in the pricing source? I imagine there could be slight differences in naming conventions (aliases, version suffixes, etc.), which might make it tricky to map usage to the correct pricing reliably. Curious if you’ve had to introduce any normalization or mapping layer to handle that, or if they line up cleanly in practice.
After taking a closer look at the tracker.py logic, I also noticed a couple of important nuances in how the local SQLite database is handled.
It looks like the tool mirrors Cursor’s internal state.vscdb into its own tracking.db using INSERT OR IGNORE. This means that once a message has been recorded locally, it is never removed again, even if it is later deleted in Cursor. However, this also implies that only messages that existed at the moment the tracker ran are ever captured — anything created and deleted between runs would never be seen.
There is also a more subtle issue with edits: since the messages table uses the message ID as the primary key and relies on INSERT OR IGNORE, any edited message that reuses the same ID will not update the stored record. As a result, only the first observed version of a message is counted, and subsequent edits are effectively ignored. This means the tracker behaves more like a “first-seen snapshot” system rather than tracking the full evolution of a message, which can lead to systematic undercounting if messages are frequently edited.
One idea that came to mind: would it make sense to include a hash of the message content (or another versioning mechanism) as part of the key, so that different edits of the same message could be tracked as distinct billable events? That might allow closer alignment with actual API usage, although I imagine it could also introduce duplication issues depending on how Cursor internally handles retries or rewrites.
Overall though, really appreciate the project — it’s a very clever approach to making Cursor usage more transparent.