{"id":44953038,"url":"https://github.com/ofershap/cursor-usage-tracker","last_synced_at":"2026-03-06T12:10:05.110Z","repository":{"id":338942552,"uuid":"1159317869","full_name":"ofershap/cursor-usage-tracker","owner":"ofershap","description":"Monitor Cursor IDE usage, detect spending anomalies, and alert your team. Self-hosted, open-source.","archived":false,"fork":false,"pushed_at":"2026-03-03T20:00:37.000Z","size":11065,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-03T21:37:21.972Z","etag":null,"topics":["ai","ai-costs","anomaly-detection","cursor","cursor-ide","enterprise","nextjs","open-source","slack-alerts","sqlite","typescript","usage-monitoring"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ofershap.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"ofershap"}},"created_at":"2026-02-16T15:32:34.000Z","updated_at":"2026-03-03T20:00:41.000Z","dependencies_parsed_at":null,"dependency_job_id":"124477a3-e0d4-47b8-bf08-577bfab3998c","html_url":"https://github.com/ofershap/cursor-usage-tracker","commit_stats":null,"previous_names":["ofershap/cursor-usage-tracker"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/ofershap/cursor-usage-tracker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofershap%2Fcursor-usage-tracker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofershap%2Fcursor-usage-tracker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofershap%2Fcursor-usage-tracker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofershap%2Fcursor-usage-tracker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ofershap","download_url":"https://codeload.github.com/ofershap/cursor-usage-tracker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ofershap%2Fcursor-usage-tracker/sbom","scorecard":{"id":1243765,"data":{"date":"2026-02-21T17:53:17Z","repo":{"name":"github.com/ofershap/cursor-usage-tracker","commit":"5848af9cf2d38daceb12e5a99f01310bf13fe673"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":5.9,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/24 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: topLevel 'security-events' permission set to 'write': .github/workflows/codeql.yml:12","Info: topLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:13","Warn: topLevel 'contents' permission set to 'write': .github/workflows/dependabot-auto-merge.yml:6","Info: topLevel 'contents' permission set to 'read': .github/workflows/release.yml:8","Info: topLevel permissions set to 'read-all': .github/workflows/scorecard.yml:9","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":5,"reason":"badge detected: Passing","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"SAST","score":9,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 6 commits out of 7 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"Pinned-Dependencies","score":2,"reason":"dependency not pinned by hash detected -- score normalized to 2","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/codeql.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dependabot-auto-merge.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/dependabot-auto-merge.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/release.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/scorecard.yml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/scorecard.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/scorecard.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/scorecard.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/ofershap/cursor-usage-tracker/scorecard.yml/main?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating node:22-alpine to node:22-alpine@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb0455466ae0bc13363826e34","Warn: containerImage not pinned by hash: Dockerfile:3","Warn: containerImage not pinned by hash: Dockerfile:8","Warn: containerImage not pinned by hash: Dockerfile:15","Warn: npmCommand not pinned by hash: .github/workflows/release.yml:26","Info:   0 out of  12 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   4 containerImage dependencies pinned","Info:   3 out of   4 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":8,"reason":"2 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-2g4f-4pwh-qvx6","Warn: Project is vulnerable to: GHSA-83g3-92jg-28cx"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/release.yml:11"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"Contributors","score":3,"reason":"project has 1 contributing companies or organizations -- score normalized to 3","details":["Info: found contributions from: elementor"],"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}},{"name":"CI-Tests","score":10,"reason":"7 out of 7 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}}]},"last_synced_at":"2026-02-21T23:15:01.807Z","repository_id":338942552,"created_at":"2026-02-21T23:15:01.808Z","updated_at":"2026-02-21T23:15:01.808Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30173331,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T07:56:45.623Z","status":"ssl_error","status_checked_at":"2026-03-06T07:55:55.621Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-costs","anomaly-detection","cursor","cursor-ide","enterprise","nextjs","open-source","slack-alerts","sqlite","typescript","usage-monitoring"],"created_at":"2026-02-18T11:09:21.676Z","updated_at":"2026-03-06T12:10:05.070Z","avatar_url":"https://github.com/ofershap.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"public/logo.png\" alt=\"Cursor Usage Tracker - open-source Cursor Enterprise cost monitoring dashboard with anomaly detection and Slack alerts\" width=\"100\" height=\"100\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eCursor Usage Tracker\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  Open-source cost monitoring and optimization for Cursor Enterprise teams. Track AI spend per developer, spot unnecessary expensive model usage, detect anomalies automatically, and get Slack alerts before the invoice surprises you. Self-host with Docker or \u003ca href=\"https://cursor-usage-tracker.sticklight.app\"\u003elet us run it for you\u003c/a\u003e.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://cursor-usage-tracker.sticklight.app\"\u003e\u003cimg src=\"https://img.shields.io/badge/🌐_Website-Hosted_\u0026_Managed-6366f1?style=for-the-badge\" alt=\"Website — Hosted \u0026 Managed\" /\u003e\u003c/a\u003e\n  \u0026nbsp;\n  \u003ca href=\"#quick-start\"\u003e\u003cimg src=\"https://img.shields.io/badge/Quick_Start-grey?style=for-the-badge\" alt=\"Quick Start\" /\u003e\u003c/a\u003e\n  \u0026nbsp;\n  \u003ca href=\"#features\"\u003e\u003cimg src=\"https://img.shields.io/badge/Features-grey?style=for-the-badge\" alt=\"Features\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ofershap/cursor-usage-tracker/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/ofershap/cursor-usage-tracker/actions/workflows/ci.yml/badge.svg\" alt=\"CI\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/ofershap/cursor-usage-tracker/actions/workflows/codeql.yml\"\u003e\u003cimg src=\"https://github.com/ofershap/cursor-usage-tracker/actions/workflows/codeql.yml/badge.svg\" alt=\"CodeQL\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://scorecard.dev/viewer/?uri=github.com/ofershap/cursor-usage-tracker\"\u003e\u003cimg src=\"https://api.scorecard.dev/projects/github.com/ofershap/cursor-usage-tracker/badge\" alt=\"OpenSSF Scorecard\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.bestpractices.dev/projects/11968\"\u003e\u003cimg src=\"https://www.bestpractices.dev/projects/11968/badge\" alt=\"OpenSSF Best Practices\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-yellow.svg\" alt=\"License: MIT\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.typescriptlang.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/TypeScript-strict-blue\" alt=\"TypeScript\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.docker.com/\"\u003e\u003cimg src=\"https://img.shields.io/badge/Docker-ready-2496ED\" alt=\"Docker\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## AI Spend Is a Blind Spot\n\nEngineering costs used to be two things: headcount and cloud infrastructure. You had tools for both. Then AI coding assistants showed up, and suddenly there's a third cost center that nobody has good tooling for.\n\nA single developer on Cursor can burn through hundreds of dollars a day just by switching to an expensive model or letting an agent loop run wild. Developers often don't know which models cost more - one of our team members used `opus-max` for weeks thinking it was a cheaper option. Now multiply that confusion by 50, 100, 500 developers. The bill gets big fast, and there's nothing like Datadog or CloudHealth for this category yet.\n\nCursor's admin dashboard shows you the raw numbers, but it won't tell you when something is off. No anomaly detection. No alerts. No incident tracking. You find out about cost spikes when the invoice lands, weeks after the damage is done.\n\nI built cursor-usage-tracker to fix that. It sits on top of Cursor's Enterprise APIs and gives engineering managers, finance, and platform teams actual visibility into AI spend before it becomes a surprise.\n\n## What This Dashboard Answers\n\n1. **Cost monitoring** - Are we spending too much? Who's driving it? Why?\n2. **Cost optimization** - Who's using expensive models when cheaper ones would do? How much would switching save?\n3. **Adoption tracking** - Is everyone using the tool we're paying for?\n4. **Usage understanding** - How is each person working with AI?\n\n---\n\n## What It Does\n\nYour company has 50+ developers on Cursor. Do you know who's spending $200/day on Claude Opus while everyone else uses Sonnet?\n\nYou're about to find out.\n\n![Cursor Usage Tracker dashboard showing team spend, anomaly detection alerts, and per-user AI cost breakdown](assets/demo.gif)\n\n\u003csub\u003eDemo animation created with \u003ca href=\"https://github.com/ofershap/remotion-readme-kit\"\u003eremotion-readme-kit\u003c/a\u003e\u003c/sub\u003e\n\n\u003cp align=\"center\"\u003e\u003ca href=\"https://www.youtube.com/watch?v=DqPjFWGI57A\"\u003e▶ Watch the full demo (90 seconds)\u003c/a\u003e\u003c/p\u003e\n\nIt connects to Cursor's Enterprise APIs, collects usage data, and automatically detects anomalies across three layers. When something looks off, you get a Slack message or email within the hour, not next month.\n\n```\nDeveloper uses Cursor → API collects data hourly → Engine detects anomaly → You get a Slack alert\n```\n\n### How It Works\n\n| What happens                                          | Example                                                                                                  |\n| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |\n| A developer exceeds the spend limit                   | `Bob spent $82 this cycle (limit: $50)` → Slack alert                                                    |\n| Someone's daily spend spikes                          | `Alice: daily spend spiked to $214 (4.2x her 7-day avg of $51)` → Slack alert                            |\n| A user's cycle spend is far above the team            | `Bob: cycle spend $957 is 5.1x the team median ($188)` → Slack alert                                     |\n| A user is statistically far from the team             | `Bob: daily spend $214 is 3.2σ above team mean ($42)` → Slack alert                                      |\n| Someone switches to an expensive model                | `Bob: cost/request spiked to $1.45 (4.2x his avg of $0.34), using opus-max` → Slack alert                |\n| A developer uses an expensive model when others don't | `Bob averaged $4.20/req on claude-opus-max (team median: $0.52 on sonnet)` → Model cost comparison table |\n\nEvery alert includes who, what model, how much, and a link to their dashboard page so you can investigate immediately.\n\n---\n\n## Features\n\n### Three-Layer Anomaly Detection\n\n| Layer               | Method        | What it catches                                                                        |\n| ------------------- | ------------- | -------------------------------------------------------------------------------------- |\n| **Thresholds**      | Static limits | Optional hard caps on spend, requests, or tokens (disabled by default)                 |\n| **Z-Score**         | Statistical   | User daily spend 2.5+ standard deviations above team mean (active users only)          |\n| **Trends**          | Spend-based   | Daily spend spikes vs personal average, cycle spend outliers vs team median            |\n| **Expensive Model** | Cost/request  | User's $/request jumps vs their own history (catches model switches like max-thinking) |\n\n### Incident Lifecycle (MTTD / MTTI / MTTR)\n\nEvery anomaly becomes a tracked incident with full lifecycle metrics:\n\n```\nAnomaly Detected ──→ Alert Sent ──→ Acknowledged ──→ Resolved\n       │                  │               │              │\n       └──── MTTD ────────┘               │              │\n                                          └── MTTI ──────┘\n       └────────────────── MTTR ─────────────────────────┘\n```\n\n- **MTTD** (Mean Time to Detect): how fast the system catches it\n- **MTTI** (Mean Time to Identify): how fast a human acknowledges it\n- **MTTR** (Mean Time to Resolve): how fast it gets fixed\n\n### Proactive Slack Notifications\n\nYou don't need to remember to check the dashboard. The system comes to you.\n\n| Notification        | When it fires                    | What you learn                                                                      |\n| ------------------- | -------------------------------- | ----------------------------------------------------------------------------------- |\n| **Anomaly alerts**  | Within the hour                  | \"Alice's daily spend spiked to $214 (4.2x her 7-day avg)\"                           |\n| **Plan exhaustion** | Daily, when users exceed plan    | \"65/151 active users have exceeded their included plan this cycle\"                  |\n| **Cycle summary**   | 3 days before billing cycle ends | Total spend, unused seats, top spenders, adoption breakdown, cycle-over-cycle trend |\n\nAnomaly alerts include severity, user, model, value vs threshold, and a direct link to the user's dashboard page. Cycle summaries tell you how many seats are going unused and who's driving cost, so you can act before the invoice lands.\n\nAlso supports **email alerts** via [Resend](https://resend.com) (one API key, no SMTP config).\n\n### Web Dashboard\n\n| Page               | What you see                                                                                                                                                        |\n| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Team Overview**  | Stat cards, spend by user, daily spend trend, spend breakdown, members table with search/sort, **group filter dropdown**, billing cycle progress, time range picker |\n| **Insights**       | DAU chart, model adoption trends, model efficiency rankings (cost/precision), MCP tool usage, file extensions, client versions                                      |\n| **User Drilldown** | Per-user token timeline, model breakdown, feature usage, activity profile, anomaly history                                                                          |\n| **Anomalies**      | Open incidents, MTTD/MTTI/MTTR metrics, full anomaly timeline                                                                                                       |\n| **Settings**       | Detection thresholds, expensive model alerts, billing group management, HiBob CSV import, group export/import                                                       |\n\n\u003e For a detailed breakdown of every section, metric, badge, and chart, see [FEATURES.md](FEATURES.md).\n\n---\n\n## Deploy\n\n### One-click deploy\n\nDeploy your own instance in minutes. You'll need a [Cursor Enterprise](https://cursor.com) plan and an Admin API key.\n\n[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/ofershap/cursor-usage-tracker)\n\n\u003e **Railway and Docker** options below. Want help setting this up for your team? Deployment, threshold tuning, first spend analysis, ongoing support. [Let's talk](https://linkedin.com/in/ofershap).\n\n---\n\n## Quick Start\n\n### Prerequisites\n\n| What                   | Where to get it                                         |\n| ---------------------- | ------------------------------------------------------- |\n| Cursor Enterprise plan | Required for API access                                 |\n| Admin API key          | Cursor dashboard → Settings → Advanced → Admin API Keys |\n| Node.js 18+            | [nodejs.org](https://nodejs.org)                        |\n\n### 1. Set up\n\n**Option A: One command**\n\n```bash\nnpx cursor-usage-tracker my-tracker\ncd my-tracker\n```\n\n**Option B: Manual clone**\n\n```bash\ngit clone https://github.com/ofershap/cursor-usage-tracker.git\ncd cursor-usage-tracker\nnpm install\n```\n\n### 2. Configure\n\n```bash\ncp .env.example .env\n```\n\nEdit `.env`:\n\n```bash\n# Required\nCURSOR_ADMIN_API_KEY=your_admin_api_key\n\n# Alerting — Slack (at least one alerting channel recommended)\nSLACK_BOT_TOKEN=xoxb-your-bot-token          # bot token with chat:write scope\nSLACK_CHANNEL_ID=C0123456789                  # channel to post alerts to\n\n# Dashboard URL (used in alert links)\nDASHBOARD_URL=http://localhost:3000\n\n# Optional\nCRON_SECRET=your_secret_here                  # protects the cron endpoint\n\n# Email alerts via Resend (optional)\nRESEND_API_KEY=re_xxxxxxxxxxxx\nALERT_EMAIL_TO=team-lead@company.com\n```\n\n### 3. Start the dashboard\n\n```bash\nnpm run dev\n# Open http://localhost:3000\n```\n\n### 4. Collect your first data\n\n```bash\nnpm run collect\n```\n\nYou should see:\n\n```\n[collect] Done in 4.2s\n  Members: 87\n  Daily usage: 30\n  Spending: 87\n  Usage events: 12,847\n```\n\n### 5. Run anomaly detection\n\nAfter collecting data, run detection separately:\n\n```bash\nnpm run detect\n```\n\nThis runs the stored data through all three detection layers and sends alerts for anything it finds.\n\n\u003e `npm run collect` only fetches data. `npm run detect` only runs detection. The cron endpoint (`POST /api/cron`) does both in one call.\n\n### 6. Set up recurring collection\n\nTrigger the cron endpoint hourly (via crontab, GitHub Actions, or any scheduler):\n\n```bash\ncurl -X POST http://localhost:3000/api/cron -H \"x-cron-secret: YOUR_SECRET\"\n```\n\nThis collects data, runs anomaly detection, and sends alerts in one call.\n\n---\n\n## Production Deployment\n\n### Docker (self-hosted)\n\n```bash\ncp .env.example .env   # configure your keys\ndocker compose up -d\n# Dashboard at http://localhost:3000\n```\n\nThe Docker image uses multi-stage builds for a minimal production image. Data persists in a Docker volume.\n\n\u003cdetails\u003e\n\u003csummary\u003eDocker Compose details\u003c/summary\u003e\n\n```yaml\nservices:\n  tracker:\n    build: .\n    ports:\n      - \"3000:3000\"\n    env_file: .env\n    volumes:\n      - tracker-data:/app/data\nvolumes:\n  tracker-data:\n```\n\n\u003c/details\u003e\n\n### Fly.io (recommended free option)\n\n```bash\nfly launch --copy-config          # creates the app from fly.toml\nfly volumes create tracker_data --region ams --size 1\nfly secrets set CURSOR_ADMIN_API_KEY=your_key CRON_SECRET=your_secret\nfly deploy\n# Dashboard at https://your-app.fly.dev\n```\n\nSet up hourly collection by adding `DASHBOARD_URL` and `CRON_SECRET` as [GitHub Actions secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions). The included `.github/workflows/cron.yml` workflow triggers `/api/cron` every hour.\n\n### Other cloud platforms\n\nAny platform that supports Docker + persistent volumes works:\n\n- **[Render](https://render.com)** - use the deploy button above, or `render.yaml` in this repo\n- **[Railway](https://railway.app)** - create a project from this repo, attach a volume at `/app/data`\n\n\u003e **Serverless platforms** (Vercel, AWS Lambda, etc.) require replacing SQLite with an external database. The data layer is abstracted behind `src/lib/data/`. Swap the implementation to use Postgres, Supabase, PlanetScale, or any other database. See [Architecture](#architecture) for details.\n\n---\n\n## Architecture\n\n```mermaid\nflowchart TB\n    APIs[\"Cursor Enterprise APIs\\n/teams/members · /teams/spend · /teams/daily-usage-data\\n/teams/filtered-usage-events · /teams/groups · /analytics/team/*\"]\n    C[\"Collector (hourly)\"]\n    DB[(\"Database\\n(SQLite default, swappable)\")]\n    D[\"Detection Engine, 3 layers\"]\n    AL[\"Alerts: Slack / Email\"]\n    DA[\"Dashboard: Next.js\"]\n\n    APIs --\u003e C --\u003e DB --\u003e D\n    DB --\u003e DA\n    D --\u003e AL\n```\n\nThe data layer is abstracted behind `src/lib/data/`. SQLite is the default (zero-config), but you can swap the implementation for Postgres, Supabase, or any database that fits your infrastructure.\n\n---\n\n## Configuration\n\nAll detection thresholds are configurable via the Settings page or the API:\n\n| Setting                   | Default | What it does                                                   |\n| ------------------------- | ------- | -------------------------------------------------------------- |\n| Max spend per cycle       | 0 (off) | Alert when a user exceeds this in a billing cycle              |\n| Max requests per day      | 0 (off) | Alert on excessive daily request count                         |\n| Max tokens per day        | 0 (off) | Alert on excessive daily token consumption                     |\n| Z-score multiplier        | 2.5     | How many standard deviations above mean to flag (spend + reqs) |\n| Z-score window            | 7 days  | Historical window for statistical comparison                   |\n| Spend spike multiplier    | 5.0x    | Alert when today's spend \u003e N× user's personal daily average    |\n| Spend spike lookback      | 7 days  | How many days of history to compare against                    |\n| Cycle outlier multiplier  | 10.0x   | Alert when cycle spend \u003e N× team median (active users only)    |\n| Cost/req spike multiplier | 3.0x    | Alert when today's $/request \u003e N× user's historical average    |\n| Cost/req min daily spend  | $20     | Skip cost/req alerts for users below this daily spend          |\n\n---\n\n## Settings\n\nThe Settings page (`/settings`) is where you configure detection behavior and manage your team structure. Everything is persisted locally and takes effect on the next detection run.\n\n### Detection Thresholds\n\nAll anomaly detection parameters listed in [Configuration](#configuration) above are editable from the Settings page. Static thresholds, z-score sensitivity, spend spike multipliers, the expensive model detector. Set any value to 0 to disable that check.\n\n### Billing Groups\n\nBilling groups let you organize team members by department, team, or any structure that fits your org. The Team Overview page has a group filter dropdown. Select a group to scope all stats, charts, and the members table to that subset.\n\nFrom the Settings page you can:\n\n- **View** all groups with member counts and per-group spend\n- **Rename** groups to match your org structure (displayed as `Parent \u003e Team`)\n- **Reassign** individual members between groups\n- **Create** new groups manually\n- **Search** across all members to find and reassign anyone\n- **Export** your current group mapping as a CSV backup\n- **Import** a previously exported CSV to restore or transfer mappings between environments\n\n### HiBob Import\n\nFor teams using [HiBob](https://www.hibob.com/) as their HR platform, the Settings page includes a dedicated **Import from HiBob** flow:\n\n1. Export a CSV from HiBob's People Directory (include Email, Department, Group, and Team columns)\n2. Upload it to the import modal\n3. Review the preview: which members move, which groups get created, who wasn't matched\n4. Selectively approve or reject individual changes before applying\n\nThe import builds a `Group \u003e Team` hierarchy automatically. Small teams (fewer than 3 members) are merged into their parent group. Members not found in the CSV keep their current assignment.\n\n\u003e The HiBob import updates your local billing groups only. It does not push changes back to HiBob or to Cursor's billing API.\n\n---\n\n## Authentication\n\nAuthentication is **fully optional**. When no auth environment variables are set, the dashboard is open (the default behavior). Setting `AUTH_SECRET` enables Google OAuth sign-in.\n\n### Setup\n\n1. Create a [Google OAuth app](https://console.cloud.google.com/apis/credentials) with redirect URI:\n   - Local: `http://localhost:3000/api/auth/callback/google`\n   - Production: `https://your-domain.com/api/auth/callback/google`\n\n2. Add to your `.env`:\n\n```bash\nAUTH_SECRET=$(openssl rand -base64 32)       # encryption key for sessions\nAUTH_GOOGLE_ID=your-client-id.apps.google... # Google OAuth client ID\nAUTH_GOOGLE_SECRET=GOCSPX-...               # Google OAuth client secret\nAUTH_TRUST_HOST=true                         # required behind a reverse proxy\nAUTH_URL=https://your-domain.com             # public URL (auto-detected locally)\n```\n\n3. Optionally restrict access by domain or specific emails:\n\n```bash\nAUTH_ALLOWED_DOMAIN=yourcompany.com          # only @yourcompany.com emails\nAUTH_ALLOWED_EMAILS=admin@example.com,cto@example.com  # or specific emails\n```\n\nWhen both are set, either match grants access. When neither is set, any Google account can sign in.\n\n### How It Works\n\n- Sessions use encrypted JWT cookies, no database tables needed\n- The `/api/cron` endpoint is excluded from auth (it uses its own `CRON_SECRET`)\n- Sign-in page appears automatically when auth is enabled\n- User avatar and sign-out menu appear in the nav bar\n\n---\n\n## API Endpoints\n\n| Endpoint              | Method  | Description                                         |\n| --------------------- | ------- | --------------------------------------------------- |\n| `/api/cron`           | POST    | Collect + detect + alert (use with scheduler)       |\n| `/api/stats`          | GET     | Dashboard statistics (`?days=7`)                    |\n| `/api/analytics`      | GET     | Analytics data: DAU, models, MCP, etc. (`?days=30`) |\n| `/api/team-spend`     | GET     | Daily team spend breakdown                          |\n| `/api/model-costs`    | GET     | Model cost breakdown by users and spend             |\n| `/api/groups`         | GET     | Billing groups with members and spend               |\n| `/api/groups`         | PATCH   | Rename group, assign member, or create group        |\n| `/api/groups/import`  | POST    | HiBob CSV import (preview + apply)                  |\n| `/api/anomalies`      | GET     | Anomaly timeline (`?days=30`)                       |\n| `/api/users/[email]`  | GET     | Per-user statistics (`?days=30`)                    |\n| `/api/incidents/[id]` | PATCH   | Acknowledge or resolve incident                     |\n| `/api/settings`       | GET/PUT | Detection configuration                             |\n\n---\n\n## Tech Stack\n\n| Component  | Technology                                                                                                                                                     |\n| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Framework  | [![Next.js](https://img.shields.io/badge/Next.js-000?logo=nextdotjs\u0026logoColor=white)](https://github.com/vercel/next.js) App Router                            |\n| Language   | [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript\u0026logoColor=white)](https://github.com/microsoft/TypeScript) strict mode           |\n| Database   | [![SQLite](https://img.shields.io/badge/SQLite-003B57?logo=sqlite\u0026logoColor=white)](https://github.com/WiseLibs/better-sqlite3) via better-sqlite3 (swappable) |\n| Charts     | [![Recharts](https://img.shields.io/badge/Recharts-22B5BF?logo=recharts\u0026logoColor=white)](https://github.com/recharts/recharts)                                |\n| Styling    | [![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-06B6D4?logo=tailwindcss\u0026logoColor=white)](https://github.com/tailwindlabs/tailwindcss)              |\n| Testing    | [![Vitest](https://img.shields.io/badge/Vitest-6E9F18?logo=vitest\u0026logoColor=white)](https://github.com/vitest-dev/vitest)                                      |\n| Deployment | [![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker\u0026logoColor=white)](https://github.com/docker) multi-stage build                               |\n\n---\n\n## Development\n\n```bash\nnpm run dev          # Start dev server\nnpm run collect      # Manual data collection\nnpm run detect       # Manual anomaly detection + alerting\nnpm run typecheck    # Type checking\nnpm test             # Run tests\nnpm run lint         # Lint + format check\n```\n\n---\n\n## Cursor API Requirements\n\nRequires a **Cursor Enterprise** plan. The tool uses these endpoints:\n\n| Endpoint                            | Auth              | What it provides                             |\n| ----------------------------------- | ----------------- | -------------------------------------------- |\n| `GET /teams/members`                | Admin API key     | Team member list                             |\n| `POST /teams/spend`                 | Admin API key     | Per-user spending data                       |\n| `POST /teams/daily-usage-data`      | Admin API key     | Daily usage metrics                          |\n| `POST /teams/filtered-usage-events` | Admin API key     | Detailed usage events with model/token info  |\n| `POST /teams/groups`                | Admin API key     | Billing groups + cycle dates                 |\n| `GET /analytics/team/*`             | Analytics API key | DAU, model usage, MCP, tabs, etc. (optional) |\n\nRate limit: 20 requests/minute (Admin API), 100 requests/minute (Analytics API). The collector handles rate limiting with automatic retry.\n\n---\n\n## Security\n\nThis project handles sensitive usage and spending data, so security matters here more than most.\n\n- **Vulnerability reporting**: See [SECURITY.md](SECURITY.md) for the disclosure policy. Report vulnerabilities privately via [GitHub Security Advisories](https://github.com/ofershap/cursor-usage-tracker/security/advisories/new), not public issues.\n- **Automated scanning**: Every push and PR goes through [CodeQL](https://codeql.github.com/) (SQL injection, XSS, CSRF, etc.) and [Dependabot](https://docs.github.com/en/code-security/dependabot) for dependency vulnerabilities.\n- **OpenSSF Scorecard**: Continuously evaluated against [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/ofershap/cursor-usage-tracker) security benchmarks.\n- **OpenSSF Best Practices**: [Passing badge](https://www.bestpractices.dev/projects/11968) earned.\n- **Data stays yours**: Everything is stored in your own infrastructure. No external services, no telemetry, no data leaving your network.\n- **Small dependency tree**: Fewer dependencies = smaller attack surface.\n- **Signed releases**: Automated via semantic-release with GitHub-verified provenance.\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for setup and guidelines. Bug reports, feature requests, docs improvements, and code are all welcome. Use [conventional commits](https://www.conventionalcommits.org/) and make sure CI is green before opening a PR.\n\n## Code of Conduct\n\nThis project uses the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md).\n\n## Author\n\n[![Made by ofershap](https://gitshow.dev/api/card/ofershap)](https://gitshow.dev/ofershap)\n\n[![LinkedIn](https://img.shields.io/badge/LinkedIn-Connect-0A66C2?style=flat\u0026logo=linkedin\u0026logoColor=white)](https://linkedin.com/in/ofershap)\n[![GitHub](https://img.shields.io/badge/GitHub-Follow-181717?style=flat\u0026logo=github\u0026logoColor=white)](https://github.com/ofershap)\n\n## License\n\n[MIT](LICENSE) © Ofer Shapira\n","funding_links":["https://github.com/sponsors/ofershap"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofershap%2Fcursor-usage-tracker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fofershap%2Fcursor-usage-tracker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofershap%2Fcursor-usage-tracker/lists"}