Skip to content

Commit aabc378

Browse files
ofershapcursoragent
andcommitted
feat: initial release — Cursor IDE usage monitoring with anomaly detection
3-layer anomaly detection (thresholds, Z-score, trends), MTTD/MTTI/MTTR incident lifecycle, Slack & email alerts, and a Next.js dashboard. - Cursor Enterprise Admin + Analytics API client (Basic Auth) - SQLite storage with zero-config setup - Detection: static thresholds, statistical outliers, spike/drift/model-shift - Dashboard: team overview, per-user drilldown, anomaly timeline, settings - Docker support with standalone Next.js output - CI/CD with GitHub Actions, semantic-release Co-authored-by: Cursor <cursoragent@cursor.com>
0 parents  commit aabc378

66 files changed

Lines changed: 16640 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
description: Coding conventions and patterns for cursor-usage-tracker
3+
globs: "**/*.{ts,tsx}"
4+
alwaysApply: false
5+
---
6+
7+
# Coding Conventions
8+
9+
## Imports
10+
11+
- NO `.js` extensions on internal imports — Next.js Turbopack bundler resolution doesn't support them
12+
- Use `@/` path alias for imports from `src/` (configured in tsconfig.json)
13+
- Use `import type` for type-only imports
14+
15+
```typescript
16+
// CORRECT
17+
import { getDb } from "@/lib/db";
18+
import type { Anomaly } from "@/lib/types";
19+
20+
// WRONG — will break Next.js build
21+
import { getDb } from "./db.js";
22+
```
23+
24+
## Database
25+
26+
- All DB access goes through `src/lib/db.ts` — never import better-sqlite3 directly in pages/routes
27+
- Use `getDb()` singleton — it initializes schema on first call
28+
- Use transactions for batch inserts: `db.transaction(() => { ... })()`
29+
- SQLite column names use snake_case; TypeScript types use camelCase
30+
31+
## API Routes
32+
33+
- All API routes use `export const dynamic = "force-dynamic"` (data is always fresh from SQLite)
34+
- Cron endpoint requires `x-cron-secret` header or `?secret=` query param
35+
- Use Next.js App Router async params pattern: `{ params }: { params: Promise<{ id: string }> }`
36+
37+
## Components
38+
39+
- Dashboard pages: server component (page.tsx) fetches data, passes to client component (*-client.tsx)
40+
- Charts are always client components ("use client")
41+
- Use Tailwind CSS with zinc color palette (dark theme)
42+
- No shadcn/ui installed — components are hand-rolled
43+
44+
## Anomaly Detection
45+
46+
- Three layers: thresholds → zscore → trends (all in `src/lib/anomaly/`)
47+
- `detector.ts` orchestrates all three, deduplicates by `userEmail:type:metric` key
48+
- New anomalies get inserted; existing ones that no longer fire get auto-resolved
49+
50+
## Testing
51+
52+
- Tests in `tests/` directory, use Vitest
53+
- Anomaly tests create their own test SQLite DB (not the main db.ts singleton)
54+
- API client tests mock `globalThis.fetch`

.cursor/rules/cursor-api.mdc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
description: Cursor Enterprise API reference — endpoints, auth, response shapes, rate limits
3+
globs: "**/cursor-client.ts"
4+
alwaysApply: false
5+
---
6+
7+
# Cursor Enterprise API Reference
8+
9+
Base URL: `https://api.cursor.com`
10+
Auth: **Basic Auth** — `Authorization: Basic {base64(API_KEY + ':')}`
11+
Two separate API keys: Admin API key and Analytics API key (generated from team settings).
12+
13+
## Rate Limits (per team, per minute)
14+
15+
| API | Endpoint Type | Limit |
16+
|-----|---------------|-------|
17+
| Admin API | Most endpoints | 20 req/min |
18+
| Admin API | `/teams/user-spend-limit` | 250 req/min |
19+
| Analytics API | Team-level (`/analytics/team/*`) | 100 req/min |
20+
| Analytics API | By-user (`/analytics/by-user/*`) | 50 req/min |
21+
22+
304 responses from ETag caching do NOT count against rate limits.
23+
24+
## Admin API Endpoints
25+
26+
### GET /teams/members
27+
Returns `{ teamMembers: [{ name, email, role }] }`
28+
29+
### POST /teams/daily-usage-data
30+
Body: `{ startDate: number (ms), endDate: number (ms) }`
31+
Poll at most once per hour (data aggregated hourly).
32+
33+
### POST /teams/spend
34+
Body: `{ page, pageSize }`
35+
Returns `{ teamMemberSpend: [{ email, spendCents, fastPremiumRequests }], subscriptionCycleStart: number, totalPages }`
36+
37+
### POST /teams/filtered-usage-events
38+
Body: `{ email?, startDate?, endDate?, page, pageSize }`
39+
Returns `{ usageEvents: [{ timestamp, model, kindLabel, tokenUsage: { inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens }, userEmail }], pagination: { hasNextPage } }`
40+
Poll at most once per hour (data aggregated hourly). Max 30 days per request.
41+
42+
### POST /teams/user-spend-limit
43+
Body: `{ email, hardLimitDollars }`
44+
45+
## Analytics API Endpoints (separate key: CURSOR_ANALYTICS_API_KEY)
46+
47+
All use `startDate`/`endDate` params (formats: `YYYY-MM-DD`, `7d`, `30d`, `today`, `yesterday`). Max 30 days. Default: last 7 days.
48+
Optional `users` param: comma-separated emails or user IDs.
49+
50+
### Team-Level
51+
- `GET /analytics/team/dau` — daily active users (includes cli_dau, cloud_agent_dau, bugbot_dau)
52+
- `GET /analytics/team/models` — model usage with `model_breakdown` per day
53+
- `GET /analytics/team/agent-edits` — agent edits (suggested/accepted diffs and lines)
54+
- `GET /analytics/team/tabs` — tab autocomplete usage
55+
- `GET /analytics/team/leaderboard` — ranked by AI usage (supports `page`, `pageSize`)
56+
- `GET /analytics/team/mcp` — MCP tool adoption
57+
- `GET /analytics/team/commands` — command adoption
58+
- `GET /analytics/team/plans` — plan mode adoption
59+
- `GET /analytics/team/ask-mode` — ask mode adoption
60+
- `GET /analytics/team/client-versions` — Cursor version distribution
61+
- `GET /analytics/team/top-file-extensions` — most edited file types
62+
63+
### By-User (paginated: `page`, `pageSize`, max 500)
64+
All follow pattern: `GET /analytics/by-user/{metric}`
65+
Available: `agent-edits`, `tabs`, `models`, `mcp`, `commands`, `plans`, `ask-mode`, `client-versions`, `top-file-extensions`
66+
67+
## Known Model Strings (from API responses)
68+
69+
`claude-sonnet-4.5`, `claude-opus-4.5`, `claude-opus-4.6`, `gpt-4o`, `gpt-5.2`, `gpt-5.3-codex`, `gemini-3-flash`, `gemini-3-pro`, `grok-code`
70+
71+
## Caching
72+
73+
Analytics and AI Code Tracking APIs support ETags. Use `If-None-Match` header for 304 responses (15-minute cache, `Cache-Control: public, max-age=900`).
74+
75+
## Important
76+
77+
Admin API endpoint response shapes are based on community implementations and may not exactly match the live API. Analytics API shapes are from official Cursor documentation (Feb 2026). If you encounter mismatches, update `src/lib/types.ts` and `src/lib/cursor-client.ts`.

.cursor/rules/project-context.mdc

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
description: Core project context for cursor-usage-tracker — architecture, data flow, key files
3+
alwaysApply: true
4+
---
5+
6+
# cursor-usage-tracker — Project Context
7+
8+
Open-source Cursor IDE usage monitoring, anomaly detection, and alerting for enterprise teams.
9+
10+
## Tech Stack
11+
12+
- Next.js (App Router, Turbopack) + TypeScript strict
13+
- SQLite via better-sqlite3 (zero-config, file at `data/tracker.db`)
14+
- Recharts for charts, Tailwind CSS for styling
15+
- Vitest for tests, ESLint 9 flat config, Prettier
16+
- Docker (multi-stage Dockerfile + docker-compose.yml)
17+
18+
## Architecture
19+
20+
```
21+
Cursor Enterprise APIs → Collector (src/lib/collector.ts) → SQLite (src/lib/db.ts)
22+
23+
Detection Engine (src/lib/anomaly/)
24+
25+
Alerts: Slack + Email (src/lib/alerts/)
26+
27+
Dashboard (src/app/ pages)
28+
```
29+
30+
Single cron endpoint `POST /api/cron` does: collect → detect → alert in one call.
31+
32+
## Key Files
33+
34+
| File | Purpose |
35+
|------|---------|
36+
| `src/lib/types.ts` | All shared types + DetectionConfig + DEFAULT_CONFIG |
37+
| `src/lib/cursor-client.ts` | Cursor API client (Admin + Analytics), pagination, rate-limit retry |
38+
| `src/lib/db.ts` | SQLite schema, all queries, dashboard stats, user stats |
39+
| `src/lib/collector.ts` | Data collection pipeline (members, daily usage, spending, events) |
40+
| `src/lib/anomaly/detector.ts` | Orchestrator — runs all 3 detection layers, deduplicates |
41+
| `src/lib/anomaly/thresholds.ts` | Layer 1: static limits (spend, requests, tokens) |
42+
| `src/lib/anomaly/zscore.ts` | Layer 2: statistical z-score vs team mean |
43+
| `src/lib/anomaly/trends.ts` | Layer 3: personal spikes, drift above P75, model shift |
44+
| `src/lib/incidents.ts` | Incident lifecycle: create, alert, acknowledge, resolve + MTTD/MTTI/MTTR |
45+
| `src/lib/alerts/slack.ts` | Slack webhook with block-kit messages |
46+
| `src/lib/alerts/email.ts` | SMTP email with HTML templates |
47+
| `src/app/api/cron/route.ts` | Main cron endpoint (collect + detect + alert) |
48+
49+
## Dashboard Pages
50+
51+
- `/` — Team overview: stat cards, spend bar chart, usage line chart, members table
52+
- `/users/[email]` — Per-user: token timeline, model pie chart, feature breakdown, anomaly history
53+
- `/anomalies` — MTTD/MTTI/MTTR metrics, open incidents (acknowledge/resolve), anomaly table
54+
- `/settings` — Configurable detection thresholds
55+
56+
## Database Tables
57+
58+
members, daily_usage, spending, usage_events, anomalies, incidents, config, collection_log
59+
60+
## Important Caveats
61+
62+
- API response shapes may not exactly match the live Cursor API. If real responses differ, update `src/lib/types.ts` and `src/lib/cursor-client.ts`.
63+
- Trend detection can produce duplicate dedup keys (spike + drift both emit `trend:tokens` for same user) — first one wins, second is silently dropped.

.dockerignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules
2+
.next
3+
.git
4+
data/*.db
5+
data/*.db-journal
6+
coverage
7+
.env
8+
.env.local

.env.example

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CURSOR_ADMIN_API_KEY=key_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2+
CURSOR_ANALYTICS_API_KEY=
3+
4+
SLACK_WEBHOOK_URL=
5+
6+
SMTP_HOST=smtp.gmail.com
7+
SMTP_PORT=587
8+
SMTP_USER=
9+
SMTP_PASS=
10+
SMTP_FROM=cursor-tracker@yourcompany.com
11+
ALERT_EMAIL_TO=team-lead@yourcompany.com
12+
13+
CRON_SECRET=your-secret-for-cron-endpoint
14+
15+
DASHBOARD_PASSWORD=

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: ofershap

.github/workflows/ci.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
node-version: [20, 22]
15+
steps:
16+
- uses: actions/checkout@v4
17+
- uses: actions/setup-node@v4
18+
with:
19+
node-version: ${{ matrix.node-version }}
20+
cache: npm
21+
- run: npm ci
22+
- run: npm run typecheck
23+
- run: npm run lint
24+
- run: npm test
25+
- run: npm run build

.github/workflows/release.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags: ["v*"]
6+
7+
permissions:
8+
contents: write
9+
issues: write
10+
pull-requests: write
11+
12+
jobs:
13+
release:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0
19+
- uses: actions/setup-node@v4
20+
with:
21+
node-version: 22
22+
cache: npm
23+
- run: npm ci
24+
- run: npm run build
25+
- run: npm test
26+
- run: npx semantic-release
27+
env:
28+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
node_modules/
2+
.next/
3+
dist/
4+
data/*.db
5+
data/*.db-journal
6+
data/*.db-shm
7+
data/*.db-wal
8+
coverage/
9+
*.tsbuildinfo
10+
.env
11+
.env.local
12+
.env.*.local
13+
.DS_Store

.husky/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npx lint-staged

0 commit comments

Comments
 (0)