Skip to content

Commit d3d2fb2

Browse files
committed
fix: replace em dashes with regular punctuation
Made-with: Cursor
1 parent 2ada3dc commit d3d2fb2

4 files changed

Lines changed: 17 additions & 17 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ ALERT_EMAIL_TO=team-lead@yourcompany.com
1111

1212
CRON_SECRET=your-secret-for-cron-endpoint
1313

14-
# Authentication (optional dashboard is open when these are not set)
14+
# Authentication (optional - dashboard is open when these are not set)
1515
# Setting AUTH_SECRET enables Google OAuth sign-in
1616
AUTH_SECRET= # openssl rand -base64 32
1717
AUTH_GOOGLE_ID= # Google OAuth client ID

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ Developer uses Cursor → API collects data hourly → Engine detects anomaly
7373
| Someone's daily spend spikes | `Alice: daily spend spiked to $214 (4.2x her 7-day avg of $51)` → Slack alert |
7474
| A user's cycle spend is far above the team | `Bob: cycle spend $957 is 5.1x the team median ($188)` → Slack alert |
7575
| A user is statistically far from the team | `Bob: daily spend $214 is 3.2σ above team mean ($42)` → Slack alert |
76-
| 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 |
76+
| 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 |
7777
| 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 |
7878

7979
Every alert includes who, what model, how much, and a link to their dashboard page so you can investigate immediately.
@@ -117,7 +117,7 @@ You don't need to remember to check the dashboard. The system comes to you.
117117
| **Plan exhaustion** | Daily, when users exceed plan | "65/151 active users have exceeded their included plan this cycle" |
118118
| **Cycle summary** | 3 days before billing cycle ends | Total spend, unused seats, top spenders, adoption breakdown, cycle-over-cycle trend |
119119

120-
Anomaly 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 costso you can act before the invoice, not after.
120+
Anomaly 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.
121121

122122
Also supports **email alerts** via [Resend](https://resend.com) (one API key, no SMTP config).
123123

@@ -143,7 +143,7 @@ Deploy your own instance in minutes. You'll need a [Cursor Enterprise](https://c
143143

144144
[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/ofershap/cursor-usage-tracker)
145145

146-
> **Railway and Docker** options below. Want help setting this up for your team — deployment, threshold tuning, first spend analysis, and ongoing support? [Let's talk](https://linkedin.com/in/ofershap).
146+
> **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).
147147
148148
---
149149

@@ -288,16 +288,16 @@ fly deploy
288288
# Dashboard at https://your-app.fly.dev
289289
```
290290

291-
Set 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.
291+
Set 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.
292292

293293
### Other cloud platforms
294294

295295
Any platform that supports Docker + persistent volumes works:
296296

297-
- **[Render](https://render.com)** use the deploy button above, or `render.yaml` in this repo
298-
- **[Railway](https://railway.app)** create a project from this repo, attach a volume at `/app/data`
297+
- **[Render](https://render.com)** - use the deploy button above, or `render.yaml` in this repo
298+
- **[Railway](https://railway.app)** - create a project from this repo, attach a volume at `/app/data`
299299

300-
> **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.
300+
> **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.
301301
302302
---
303303

@@ -317,7 +317,7 @@ flowchart TB
317317
D --> AL
318318
```
319319

320-
The 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.
320+
The 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.
321321

322322
---
323323

@@ -346,11 +346,11 @@ The Settings page (`/settings`) is where you configure detection behavior and ma
346346

347347
### Detection Thresholds
348348

349-
All anomaly detection parameters listed in [Configuration](#configuration) above are editable from the Settings page — static thresholds, z-score sensitivity, spend spike multipliers, and the expensive model detector. Set any value to 0 to disable that specific check.
349+
All 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.
350350

351351
### Billing Groups
352352

353-
Billing groups let you organize team members by department, team, or any structure that fits your org. The Team Overview page includes a group filter dropdown — select a group to instantly scope all stats, charts, and the members table to that subset.
353+
Billing 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.
354354

355355
From the Settings page you can:
356356

@@ -368,7 +368,7 @@ For teams using [HiBob](https://www.hibob.com/) as their HR platform, the Settin
368368

369369
1. Export a CSV from HiBob's People Directory (include Email, Department, Group, and Team columns)
370370
2. Upload it to the import modal
371-
3. Review the preview — see which members will be moved, which groups will be created, and who wasn't matched
371+
3. Review the preview: which members move, which groups get created, who wasn't matched
372372
4. Selectively approve or reject individual changes before applying
373373

374374
The import builds a `Group > 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.
@@ -408,7 +408,7 @@ When both are set, either match grants access. When neither is set, any Google a
408408

409409
### How It Works
410410

411-
- Sessions use encrypted JWT cookies no database tables needed
411+
- Sessions use encrypted JWT cookies, no database tables needed
412412
- The `/api/cron` endpoint is excluded from auth (it uses its own `CRON_SECRET`)
413413
- Sign-in page appears automatically when auth is enabled
414414
- User avatar and sign-out menu appear in the nav bar

src/app/settings/settings-client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export function SettingsClient({ config: initial }: SettingsClientProps) {
171171

172172
<Section
173173
title="Expensive Model Detection"
174-
description="Alert when a user's $/request jumps — catches switches to expensive models like max-thinking"
174+
description="Alert when a user's $/request jumps. Catches switches to expensive models like max-thinking."
175175
>
176176
<Field
177177
label="Cost/req spike multiplier"

src/lib/anomaly/trends.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ function detectSpendSpikes(
6363
metric: "spend",
6464
value: user.spend_cents,
6565
threshold: history.avg_spend * spikeMultiplier,
66-
message: `${user.name}: daily spend spiked to $${todayDollars} (${ratio.toFixed(1)}x their ${lookbackDays}-day avg of $${avgDollars}) model: ${user.most_used_model || "unknown"}`,
66+
message: `${user.name}: daily spend spiked to $${todayDollars} (${ratio.toFixed(1)}x their ${lookbackDays}-day avg of $${avgDollars}), model: ${user.most_used_model || "unknown"}`,
6767
detectedAt: now,
6868
resolvedAt: null,
6969
alertedAt: null,
@@ -98,7 +98,7 @@ function detectCycleOutliers(anomalies: Anomaly[], now: string, outlierMultiplie
9898
metric: "spend",
9999
value: user.spend_cents,
100100
threshold: median * outlierMultiplier,
101-
message: `${user.name}: cycle spend $${userDollars} is ${ratio.toFixed(1)}x the team median ($${medianDollars}) model: ${user.most_used_model || "unknown"}, ${user.fast_premium_requests} premium reqs`,
101+
message: `${user.name}: cycle spend $${userDollars} is ${ratio.toFixed(1)}x the team median ($${medianDollars}), model: ${user.most_used_model || "unknown"}, ${user.fast_premium_requests} premium reqs`,
102102
detectedAt: now,
103103
resolvedAt: null,
104104
alertedAt: null,
@@ -140,7 +140,7 @@ function detectCostPerReqSpikes(
140140
metric: "cost_per_req",
141141
value: Math.round(user.today_cost_per_req),
142142
threshold: Math.round(user.hist_avg_cost_per_req * spikeMultiplier),
143-
message: `${user.name}: cost/request spiked to $${todayCpr} (${ratio.toFixed(1)}x their avg of $${histCpr}) using ${user.today_top_model || "unknown"}, $${todaySpend} total today across ${user.today_reqs} reqs`,
143+
message: `${user.name}: cost/request spiked to $${todayCpr} (${ratio.toFixed(1)}x their avg of $${histCpr}), using ${user.today_top_model || "unknown"}, $${todaySpend} total today across ${user.today_reqs} reqs`,
144144
detectedAt: now,
145145
resolvedAt: null,
146146
alertedAt: null,

0 commit comments

Comments
 (0)