Skip to content

feat: add Cursor quota provider#132

Open
Daltonganger wants to merge 6 commits intoopgginc:mainfrom
Daltonganger:feature/cursor-provider
Open

feat: add Cursor quota provider#132
Daltonganger wants to merge 6 commits intoopgginc:mainfrom
Daltonganger:feature/cursor-provider

Conversation

@Daltonganger
Copy link
Copy Markdown
Contributor

Adds Cursor as a quota-based provider using the local SQLite/API extraction approach from mm7894215/TokenTracker.

Summary:

  • Reads Cursor session data from ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb and ~/.cursor/cli-config.json without logging tokens.
  • Calls https://cursor.com/api/usage-summary with the Cursor session cookie and normalizes Cursor quota windows.
  • Shows Cursor as a quota/subscription provider in the menu, status bar, CLI provider list, and subscription settings.
  • Displays Cursor quota as Auto and API percentages only, while keeping the plan name as context.
  • Adds the official Cursor cube SVG logo asset as a template vector icon.
  • Adds unit coverage for Cursor quota normalization, flexible numeric decoding, and user ID extraction.

Validation:

  • CursorProviderTests: 7 tests, 0 failures.
  • Full Debug test suite: 114 tests, 9 skipped, 0 failures.
  • Debug build succeeded.
  • Launched Debug app and confirmed Cursor provider logs, menu update completion, and visible row: Cursor: 1%, 54% with Auto/API windows and Using in: Cursor.

Related fork PR: Daltonganger#3

@op-gg-ai-devops
Copy link
Copy Markdown
Contributor

op-gg-ai-devops Bot commented Apr 29, 2026

✅ AI Code Review Completed

Review finished. Check the PR for inline comments.


📋 View Logs | 🤖 Model: anthropic/claude-opus-4-7|high

Copy link
Copy Markdown
Contributor

@op-gg-ai-devops op-gg-ai-devops Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly this is a clean addition — the JSON model with snake/camel fallback decoders, the flexible Double parser, and the team-pool resolution logic are all thoughtful. tests cover the interesting edges (used/limit fallback, team pool selection, JWT/CLI userId extraction, percent clamping). reading the diff felt like reading the existing providers, which is the highest compliment a new provider can earn here.

few things hold this back from a clean approve though:

Blockers

  • SwiftLint is failing on this PR because of a duplicate import Foundation at the top of CursorProvider.swift. that's also why CI is red. one-line fix.

Worth fixing in this PR

  • Force-unwrapped URL(string:)! on line 326 — the codebase already has a precedent (see AGENTS.md → "TimeZone Force Unwrap Safety") for avoiding force-unwraps even on hardcoded literals. swap to a static let or guard let.
  • Dead branch in ModernStatusBarIconView.swift:127 — the case .cursor: return "cursorarrow" is unreachable because the caller always short-circuits on identifier == .cursor to render the asset image. remove it so future devs don't get confused about which icon wins.

Minor / non-blocking observations

  • cursorAutoReset and cursorApiReset are always set to the same billingCycleEnd value. fine for now, but if Cursor ever exposes per-window resets the model is already shaped for it — nice forward thinking.
  • headlinePlanPercent returns 0.0 when totalPercentUsed is exactly 0, which then triggers the (percent ?? 0) == 0 fallback to on-demand. that means a real "0% used" plan can silently hop to the on-demand bucket. probably what you want for the headline metric, but worth a // MARK: comment so the next reader doesn't have to puzzle it out.
  • No minimumFetchInterval set, so this hits cursor.com on every refresh tick. consistent with most providers; only ClaudeProvider throttles. low priority.
  • the SQLite read uses the default mode — if Cursor itself is writing while we read, you can hit "database is locked". the error path surfaces it as providerError, so it'll show up as an error row instead of a crash. acceptable, but PRAGMA busy_timeout=2000 or -readonly mode would harden it later.

Validation

the test suite + manual launch evidence in the PR body is exactly the kind of signal that makes review fast. thanks for that.


once the duplicate import is gone CI should go green and this is good to merge. nice work overall.


This bot will re-review automatically on new pushes. You can also re-trigger it by mentioning @op-gg-ai-devops in a comment with optional directions, e.g.:

  • @op-gg-ai-devops review typos
  • @op-gg-ai-devops focus on security in CursorProvider.swift
  • @op-gg-ai-devops re-check after fixing duplicate import

Comment thread CopilotMonitor/CopilotMonitor/Providers/CursorProvider.swift Outdated
Comment thread CopilotMonitor/CopilotMonitor/Providers/CursorProvider.swift Outdated
Comment thread CopilotMonitor/CopilotMonitor/Views/SwiftUI/ModernStatusBarIconView.swift Outdated
@op-gg-ai-devops
Copy link
Copy Markdown
Contributor

op-gg-ai-devops Bot commented Apr 29, 2026

✅ AI Code Review Completed

Review finished. Check the PR for inline comments.


📋 View Logs | 🤖 Model: anthropic/claude-opus-4-7|high

Copy link
Copy Markdown
Contributor

@op-gg-ai-devops op-gg-ai-devops Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all three blockers from the previous round are gone:

  • duplicate import Foundation → dropped, SwiftLint is green now
  • force-unwrapped URL literal → swapped for a proper guard let
  • dead case .cursor: return "cursorarrow" → killed, systemIconName(for:) now returns String? and the asset image branch handles .cursor cleanly

the inline comment you added on the 0%-headline → on-demand fallback is exactly what i was hoping for. future-me reading that switch will know it's intentional.

also the small things that didn't get touched are fine — cursorAutoReset == cursorApiReset == billingCycleEnd is forward-shaped for when Cursor exposes per-window resets, no minimumFetchInterval keeps you consistent with most providers, and the SQLite default-mode read still degrades gracefully via providerError.

CI signal: SwiftLint ✅, GitHub Actions Lint ✅. Build is still running but with the SwiftLint fix in there's no reason to expect red.

One small thing for later (non-blocking)

Cursor's actual paid tier list is wider than what's in ProviderSubscriptionPresets.cursor:

Plan Price
Pro $20/mo ✅ already in presets
Pro+ $60/mo ❌ missing
Ultra $200/mo ❌ missing
Teams $40/user/mo ✅ already in presets

users on Pro+ or Ultra won't have a matching preset. fine to leave for a follow-up — not a blocker for this PR.

Validation

tests cover the interesting bits (used/limit fallback, team pool selection, JWT/CLI userId extraction, percent clamping). manual launch evidence in the PR body is appreciated.

ship it. nice work — turning around three review fixes plus a clarifying comment that fast is solid.


This bot will re-review automatically on new pushes. You can also re-trigger it by mentioning @op-gg-ai-devops in a comment with optional directions, e.g.:

  • @op-gg-ai-devops review typos
  • @op-gg-ai-devops focus on security in CursorProvider.swift
  • @op-gg-ai-devops re-check after merging main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant