Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CopilotMonitor/CLI/CLIProviderManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ actor CLIProviderManager {

static let registeredProviders: [ProviderIdentifier] = [
.claude, .codex, .cursor, .geminiCLI, .openRouter,
.antigravity, .openCodeZen, .openCodeGo, .grok, .kimi, .minimaxCodingPlan, .zaiCodingPlan,
.antigravity, .openCodeZen, .openCodeGo, .kiro, .grok, .kimi, .minimaxCodingPlan, .zaiCodingPlan,
.nanoGpt,
.chutes, .copilot,
.synthetic
Expand All @@ -33,6 +33,7 @@ actor CLIProviderManager {
let antigravityProvider = AntigravityProvider()
let openCodeZenProvider = OpenCodeZenProvider()
let openCodeGoProvider = OpenCodeGoProvider()
let kiroProvider = KiroProvider()
let grokProvider = GrokProvider()
let kimiProvider = KimiProvider()
let minimaxProvider = MiniMaxProvider()
Expand All @@ -53,6 +54,7 @@ actor CLIProviderManager {
antigravityProvider,
openCodeZenProvider,
openCodeGoProvider,
kiroProvider,
grokProvider,
kimiProvider,
minimaxProvider,
Expand Down
10 changes: 10 additions & 0 deletions CopilotMonitor/CopilotMonitor.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
MINIMAXAPP11111111111111 /* MiniMaxProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = MINIMAXFILE2222222222222 /* MiniMaxProvider.swift */; };
OCGOAPP11111111111111 /* OpenCodeGoProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = OCGOFILE2222222222222 /* OpenCodeGoProvider.swift */; };
GROKAPP11111111111111 /* GrokProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = GROKFILE2222222222222 /* GrokProvider.swift */; };
KIROAPP11111111111111 /* KiroProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = KIROFILE2222222222222 /* KiroProvider.swift */; };
NANOGPTAPP11111111111111 /* NanoGptProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = NANOGPTFILE222222222222 /* NanoGptProvider.swift */; };
A55555555555555555555555 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A66666666666666666666666 /* Assets.xcassets */; };
AD95EBD6AE3134DF4C797577 /* ClaudeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DDC1B4DE6B5118CE4AE8F82 /* ClaudeProvider.swift */; };
Expand All @@ -59,6 +60,7 @@
CLIMINIMAX11111111111111 /* MiniMaxProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = MINIMAXFILE2222222222222 /* MiniMaxProvider.swift */; };
OCGOCLI11111111111111 /* OpenCodeGoProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = OCGOFILE2222222222222 /* OpenCodeGoProvider.swift */; };
GROKCLI11111111111111 /* GrokProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = GROKFILE2222222222222 /* GrokProvider.swift */; };
KIROCLI11111111111111 /* KiroProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = KIROFILE2222222222222 /* KiroProvider.swift */; };
CLIZAI11111111111111111 /* ZaiCodingPlanProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A454D8C32F30548900E355E3 /* ZaiCodingPlanProvider.swift */; };
NANOGPTCLI11111111111111 /* NanoGptProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = NANOGPTFILE222222222222 /* NanoGptProvider.swift */; };
CLIOPENCZEN1111111111111 /* OpenCodeZenProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = OZ2222222222222222222222 /* OpenCodeZenProvider.swift */; };
Expand All @@ -77,6 +79,7 @@
MINIMAXTESTBF11111111111 /* MiniMaxProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = MINIMAXTESTFR11111111111 /* MiniMaxProviderTests.swift */; };
OCGOTESTBF11111111111 /* OpenCodeGoProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OCGOTESTFR11111111111 /* OpenCodeGoProviderTests.swift */; };
GROKTESTBF11111111111 /* GrokProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = GROKTESTFR11111111111 /* GrokProviderTests.swift */; };
KIROTESTBF11111111111 /* KiroProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = KIROTESTFR11111111111 /* KiroProviderTests.swift */; };
MINIMAXJSONBF11111111111 /* minimax_response.json in Resources */ = {isa = PBXBuildFile; fileRef = MINIMAXJSONFR11111111111 /* minimax_response.json */; };
BRAVEAPP1111111111111111 /* BraveSearchProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = BRAVEFILE22222222222222 /* BraveSearchProvider.swift */; };
TAVILYAPP111111111111111 /* TavilySearchProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = TAVILYFILE22222222222222 /* TavilySearchProvider.swift */; };
Expand Down Expand Up @@ -170,6 +173,7 @@
MINIMAXFILE2222222222222 /* MiniMaxProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMaxProvider.swift; sourceTree = "<group>"; };
OCGOFILE2222222222222 /* OpenCodeGoProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCodeGoProvider.swift; sourceTree = "<group>"; };
GROKFILE2222222222222 /* GrokProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrokProvider.swift; sourceTree = "<group>"; };
KIROFILE2222222222222 /* KiroProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KiroProvider.swift; sourceTree = "<group>"; };
NANOGPTFILE222222222222 /* NanoGptProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NanoGptProvider.swift; sourceTree = "<group>"; };
A66666666666666666666666 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
A77777777777777777777777 /* OpenCode Bar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "OpenCode Bar.app"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -199,6 +203,7 @@
MINIMAXTESTFR11111111111 /* MiniMaxProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MiniMaxProviderTests.swift; sourceTree = "<group>"; };
OCGOTESTFR11111111111 /* OpenCodeGoProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenCodeGoProviderTests.swift; sourceTree = "<group>"; };
GROKTESTFR11111111111 /* GrokProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GrokProviderTests.swift; sourceTree = "<group>"; };
KIROTESTFR11111111111 /* KiroProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KiroProviderTests.swift; sourceTree = "<group>"; };
MINIMAXJSONFR11111111111 /* minimax_response.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = minimax_response.json; sourceTree = "<group>"; };
BRAVEFILE22222222222222 /* BraveSearchProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BraveSearchProvider.swift; sourceTree = "<group>"; };
TAVILYFILE22222222222222 /* TavilySearchProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TavilySearchProvider.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -287,6 +292,7 @@
OZ2222222222222222222222 /* OpenCodeZenProvider.swift */,
OCGOFILE2222222222222 /* OpenCodeGoProvider.swift */,
GROKFILE2222222222222 /* GrokProvider.swift */,
KIROFILE2222222222222 /* KiroProvider.swift */,
KI2222222222222222222222 /* KimiProvider.swift */,
MINIMAXFILE2222222222222 /* MiniMaxProvider.swift */,
283349002F313176004DADE1 /* ChutesProvider.swift */,
Expand Down Expand Up @@ -441,6 +447,7 @@
MINIMAXTESTFR11111111111 /* MiniMaxProviderTests.swift */,
OCGOTESTFR11111111111 /* OpenCodeGoProviderTests.swift */,
GROKTESTFR11111111111 /* GrokProviderTests.swift */,
KIROTESTFR11111111111 /* KiroProviderTests.swift */,
CURSORTSTFR11111111111 /* CursorProviderTests.swift */,
TOKENTESTFR1111111111111 /* TokenManagerTests.swift */,
CODEXTESTFR111111111111 /* CodexProviderTests.swift */,
Expand Down Expand Up @@ -615,6 +622,7 @@
CLIMINIMAX11111111111111 /* MiniMaxProvider.swift in Sources */,
OCGOCLI11111111111111 /* OpenCodeGoProvider.swift in Sources */,
GROKCLI11111111111111 /* GrokProvider.swift in Sources */,
KIROCLI11111111111111 /* KiroProvider.swift in Sources */,
CLIZAI11111111111111111 /* ZaiCodingPlanProvider.swift in Sources */,
NANOGPTCLI11111111111111 /* NanoGptProvider.swift in Sources */,
283349022F313176004DADE1 /* ChutesProvider.swift in Sources */,
Expand Down Expand Up @@ -661,6 +669,7 @@
MINIMAXAPP11111111111111 /* MiniMaxProvider.swift in Sources */,
OCGOAPP11111111111111 /* OpenCodeGoProvider.swift in Sources */,
GROKAPP11111111111111 /* GrokProvider.swift in Sources */,
KIROAPP11111111111111 /* KiroProvider.swift in Sources */,
283349012F313176004DADE1 /* ChutesProvider.swift in Sources */,
TAVILYAPP111111111111111 /* TavilySearchProvider.swift in Sources */,
BRAVEAPP1111111111111111 /* BraveSearchProvider.swift in Sources */,
Expand Down Expand Up @@ -693,6 +702,7 @@
MINIMAXTESTBF11111111111 /* MiniMaxProviderTests.swift in Sources */,
OCGOTESTBF11111111111 /* OpenCodeGoProviderTests.swift in Sources */,
GROKTESTBF11111111111 /* GrokProviderTests.swift in Sources */,
KIROTESTBF11111111111 /* KiroProviderTests.swift in Sources */,
CURSORTSTBF11111111111 /* CursorProviderTests.swift in Sources */,
TOKENTESTBF1111111111111 /* TokenManagerTests.swift in Sources */,
CODEXTESTBF111111111111 /* CodexProviderTests.swift in Sources */,
Expand Down
5 changes: 5 additions & 0 deletions CopilotMonitor/CopilotMonitor/App/StatusBarController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,8 @@
add(details?.openCodeGoMonthlyUsage, priority: .monthly)
add(details?.sevenDayUsage, priority: .weekly)
add(details?.fiveHourUsage, priority: .hourly)
case .kiro:
add(usage.usagePercentage, priority: .monthly)
case .grok:
add(details?.monthlyUsage, priority: .monthly)
case .codex:
Expand Down Expand Up @@ -1948,6 +1950,7 @@
.kimi,
.minimaxCodingPlan,
.openCodeGo,
.kiro,
.grok,
.codex,
.cursor,
Expand Down Expand Up @@ -3045,6 +3048,8 @@
image = NSImage(named: "OpencodeIcon")
case .openCodeGo:
image = NSImage(named: "OpencodeIcon")
case .kiro:
image = NSImage(named: "KiroIcon")
case .grok:
image = NSImage(named: "GrokIcon")
case .kimi:
Expand Down Expand Up @@ -3825,7 +3830,7 @@
// 3. OpenRouter - only has current cost, no daily history
// We'll include today's cost if available
if let routerResult = providerResults[.openRouter],
case .payAsYouGo(_, let cost, _) = routerResult.usage,

Check warning on line 3833 in CopilotMonitor/CopilotMonitor/App/StatusBarController.swift

View workflow job for this annotation

GitHub Actions / Build & Test

immutable value 'cost' was never used; consider replacing with '_' or removing it

Check warning on line 3833 in CopilotMonitor/CopilotMonitor/App/StatusBarController.swift

View workflow job for this annotation

GitHub Actions / Build & Test

immutable value 'cost' was never used; consider replacing with '_' or removing it
let dailyCost = routerResult.details?.dailyUsage {
let today = Calendar.current.startOfDay(for: Date())
if aggregatedDailyCosts[today] == nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "kiro-ghost.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions CopilotMonitor/CopilotMonitor/Helpers/ProviderMenuBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,53 @@ extension StatusBarController {
addSubscriptionItems(to: submenu, provider: .synthetic, accountId: subscriptionAccountId)
debugLog("createDetailSubmenu: added subscription items for Synthetic")

case .kiro:
if let total = details.creditsTotal, total > 0, let remaining = details.creditsRemaining {
let used = max(total - remaining, 0)
let usagePercent = min(max((used / total) * 100.0, 0), 999)
let rows = createUsageWindowRow(
label: "Monthly Credits",
usagePercent: usagePercent,
resetDate: details.primaryReset,
isMonthly: true
)
rows.forEach { submenu.addItem($0) }

let usedItem = NSMenuItem()
usedItem.view = createDisabledLabelView(
text: String(format: "Credits Used: %.2f / %.2f", used, total),
icon: NSImage(systemSymbolName: "creditcard", accessibilityDescription: "Credits")
)
submenu.addItem(usedItem)

let remainingItem = NSMenuItem()
remainingItem.view = createDisabledLabelView(text: String(format: "Credits Left: %.2f", remaining))
submenu.addItem(remainingItem)
}

if let bonusUsage = details.secondaryUsage {
let rows = createUsageWindowRow(
label: "Bonus Credits",
usagePercent: bonusUsage,
resetDate: details.secondaryReset,
isMonthly: false
)
rows.forEach { submenu.addItem($0) }
}

if let plan = details.planType {
let planItem = NSMenuItem()
planItem.view = createDisabledLabelView(
text: "Plan: \(plan)",
icon: NSImage(systemSymbolName: "crown", accessibilityDescription: "Plan")
)
submenu.addItem(planItem)
}

submenu.addItem(NSMenuItem.separator())
addSubscriptionItems(to: submenu, provider: .kiro, accountId: subscriptionAccountId)
debugLog("createDetailSubmenu: added subscription items for Kiro")

default:
break
}
Expand Down
7 changes: 7 additions & 0 deletions CopilotMonitor/CopilotMonitor/Models/ProviderProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum ProviderIdentifier: String, CaseIterable {
case antigravity
case openCodeZen = "opencode_zen"
case openCodeGo = "opencode_go"
case kiro
case grok
case kimi
case minimaxCodingPlan = "minimax_coding_plan"
Expand Down Expand Up @@ -52,6 +53,8 @@ enum ProviderIdentifier: String, CaseIterable {
return "OpenCode Zen"
case .openCodeGo:
return "OpenCode Go"
case .kiro:
return "Kiro"
case .grok:
return "Grok"
case .kimi:
Expand Down Expand Up @@ -95,6 +98,8 @@ enum ProviderIdentifier: String, CaseIterable {
return "Zen"
case .openCodeGo:
return "Go"
case .kiro:
return "Kiro"
case .grok:
return "Grok"
case .kimi:
Expand Down Expand Up @@ -138,6 +143,8 @@ enum ProviderIdentifier: String, CaseIterable {
return "moon.stars"
case .openCodeGo:
return "chevron.left.forwardslash.chevron.right"
case .kiro:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor: Style k.circle collides with Kimi icon
Kimi already uses k.circle (see the .kimi case right below in MultiProviderStatusBarIconView). Two providers with the same SF symbol means a user running both can't visually tell their status-bar alerts apart. Pick a Kiro-distinct symbol — e.g. keyboard.badge.eye, cpu, or a custom asset — and apply it consistently across ProviderIdentifier.iconName, MultiProviderStatusBarIconView, and ModernStatusBarIconView.

return "KiroIcon"
case .grok:
return "GrokIcon"
case .kimi:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ struct ProviderSubscriptionPresets {
static let openCodeGo: [SubscriptionPreset] = [
SubscriptionPreset(name: "Go", cost: 10)
]
static let kiro: [SubscriptionPreset] = [
SubscriptionPreset(name: "Free", cost: 0),
SubscriptionPreset(name: "Pro", cost: 20),
SubscriptionPreset(name: "Pro+", cost: 40),
SubscriptionPreset(name: "Power", cost: 200)
Comment on lines +178 to +182
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor: Bug preset names won't match parsed plan strings
The parser captures plan names like KIRO PRO, KIRO POWER (uppercase, with the KIRO prefix) from the CLI output, but the presets you added below name them Pro, Pro+, Power:

static let kiro: [SubscriptionPreset] = [
    SubscriptionPreset(name: "Free", cost: 0),
    SubscriptionPreset(name: "Pro", cost: 20),
    SubscriptionPreset(name: "Pro+", cost: 40),
    SubscriptionPreset(name: "Power", cost: 200)
]

Depending on how preset matching is done (looks like name-based in ProviderSubscriptionPresets.presets(for:) consumers), this likely won't auto-resolve the right preset. Two clean options:

  1. Normalize the parsed plan name (strip KIRO prefix, lowercase, title-case) in parsePlanName.
  2. Rename presets to match parsed output: KIRO FREE, KIRO PRO, KIRO PRO+, KIRO POWER.

Worth picking whichever matches how other providers (Grok, Kimi) handle the same translation.

]
static let grok: [SubscriptionPreset] = [
SubscriptionPreset(name: "SuperGrok Lite", cost: 10),
SubscriptionPreset(name: "SuperGrok", cost: 30),
Expand Down Expand Up @@ -210,6 +216,8 @@ struct ProviderSubscriptionPresets {
return openCodeZen
case .openCodeGo:
return openCodeGo
case .kiro:
return kiro
case .grok:
return grok
case .tavilySearch:
Expand Down
Loading
Loading