-
Notifications
You must be signed in to change notification settings - Fork 385
fix: use tier-aware effective budget for Cursor usage percentage #233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
KalnuErelis
wants to merge
1
commit into
steipete:main
from
KalnuErelis:fix/cursor-usage-tier-aware-percentage
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
Sources/CodexBarCore/Providers/Cursor/CursorEffectiveUsage.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import Foundation | ||
|
|
||
| /// Effective usage snapshot for Cursor plans with tier-aware budget calculations. | ||
| /// This provides accurate usage percentages for higher-tier plans (Pro+, Ultra) | ||
| /// that have effective budgets much larger than their nominal prices. | ||
| public struct CursorEffectiveUsage: Codable, Sendable { | ||
| /// Total usage (plan + on-demand) in USD | ||
| public let totalUsedUSD: Double | ||
| /// Effective budget (tier budget + on-demand limit) in USD | ||
| public let effectiveBudgetUSD: Double | ||
| /// Plan tier for display purposes | ||
| public let planTier: CursorPlanTier | ||
| /// Whether on-demand usage has started (plan allowance exhausted) | ||
| public let isPlanExhausted: Bool | ||
| /// On-demand usage in USD (for separate display when plan is exhausted) | ||
| public let onDemandUsedUSD: Double | ||
| /// On-demand limit in USD (nil if unlimited) | ||
| public let onDemandLimitUSD: Double? | ||
|
|
||
| public init( | ||
| totalUsedUSD: Double, | ||
| effectiveBudgetUSD: Double, | ||
| planTier: CursorPlanTier, | ||
| isPlanExhausted: Bool, | ||
| onDemandUsedUSD: Double, | ||
| onDemandLimitUSD: Double?) | ||
| { | ||
| self.totalUsedUSD = totalUsedUSD | ||
| self.effectiveBudgetUSD = effectiveBudgetUSD | ||
| self.planTier = planTier | ||
| self.isPlanExhausted = isPlanExhausted | ||
| self.onDemandUsedUSD = onDemandUsedUSD | ||
| self.onDemandLimitUSD = onDemandLimitUSD | ||
| } | ||
|
|
||
| /// Effective percentage used based on total usage / effective budget | ||
| public var effectivePercentUsed: Double { | ||
| guard self.effectiveBudgetUSD > 0 else { return 0 } | ||
| return min((self.totalUsedUSD / self.effectiveBudgetUSD) * 100, 100) | ||
| } | ||
| } |
81 changes: 81 additions & 0 deletions
81
Sources/CodexBarCore/Providers/Cursor/CursorPlanTier.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import Foundation | ||
|
|
||
| /// Represents Cursor subscription plan tiers with their effective usage budgets. | ||
| /// | ||
| /// Higher-tier plans provide more effective capacity than their nominal price suggests. | ||
| /// For example, Ultra ($200/mo) provides approximately 20x the value of a baseline Pro plan, | ||
| /// meaning users can consume ~$800 worth of API usage before hitting limits. | ||
| public enum CursorPlanTier: String, Codable, Sendable { | ||
| case hobby | ||
| case pro | ||
| case proPlus | ||
| case ultra | ||
| case team | ||
| case enterprise | ||
| case unknown | ||
|
|
||
| /// Initialize plan tier from the API's membership type string. | ||
| /// - Parameter membershipType: The membership type from Cursor's API (e.g., "pro", "pro+", "ultra") | ||
| public init(membershipType: String?) { | ||
| switch membershipType?.lowercased() { | ||
| case "hobby": | ||
| self = .hobby | ||
| case "pro": | ||
| self = .pro | ||
| case "pro+", "pro_plus", "proplus": | ||
| self = .proPlus | ||
| case "ultra": | ||
| self = .ultra | ||
| case "team": | ||
| self = .team | ||
| case "enterprise": | ||
| self = .enterprise | ||
| default: | ||
| self = .unknown | ||
| } | ||
| } | ||
|
|
||
| /// Effective budget in USD based on plan tier. | ||
| /// | ||
| /// These values approximate the actual usage capacity provided by each tier, | ||
| /// calibrated against a ~$40 Pro baseline: | ||
| /// - Pro ($20/mo) provides ~$40 effective (1x baseline) | ||
| /// - Pro+ ($60/mo) provides ~$120 effective (3x baseline) | ||
| /// - Ultra ($200/mo) provides ~$800 effective (20x baseline) | ||
| public var effectiveBudgetUSD: Double { | ||
| switch self { | ||
| case .hobby: | ||
| return 20 | ||
| case .pro: | ||
| return 40 | ||
| case .proPlus: | ||
| return 120 // 3x baseline | ||
| case .ultra: | ||
| return 800 // 20x baseline | ||
| case .team: | ||
| return 60 | ||
| case .enterprise, .unknown: | ||
| return 40 // Conservative default | ||
| } | ||
| } | ||
|
|
||
| /// Display name for the plan tier. | ||
| public var displayName: String { | ||
| switch self { | ||
| case .hobby: | ||
| return "Hobby" | ||
| case .pro: | ||
| return "Pro" | ||
| case .proPlus: | ||
| return "Pro+" | ||
| case .ultra: | ||
| return "Ultra" | ||
| case .team: | ||
| return "Team" | ||
| case .enterprise: | ||
| return "Enterprise" | ||
| case .unknown: | ||
| return "Unknown" | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching the primary meter to
effectivePercentUsedmeans the primary now includes on‑demand capacity, soprimary.remainingPercentwill stay > 0 until both plan and on‑demand limits are fully consumed. The remaining‑view logic for Cursor explicitly relies onprimary.remainingPercent <= 0to fall back to the on‑demand window (seeUsageSnapshot.switcherWeeklyWindowinSources/CodexBarCore/UsageFetcher.swiftlines 146‑161), so after this change a plan can be exhausted and on‑demand in use but the UI will never switch to the on‑demand remaining view. Example: plan used $40 with a $100 on‑demand limit yields ~35.7% primary usage, so the fallback never triggers. Consider usingisPlanExhaustedor the legacy plan percent for that switch so remaining‑mode still shows on‑demand when the plan is exhausted.Useful? React with 👍 / 👎.