| name | codexu-macos-usage-tracker |
| description | macOS menu bar app for tracking OpenAI Codex and Claude Code quota, token usage, and task management with local-first analytics |
| triggers | ["how do I install and configure codexU on macOS","show me codexU usage tracking and quota monitoring","integrate codexU with Codex or Claude Code on my Mac","customize codexU menu bar display and settings","build and package codexU from source","troubleshoot codexU quota display or token statistics","use codexU API to access local Codex usage data","configure codexU status bar widgets and shortcuts"] |
codexU macOS Usage Tracker
Skill by ara.so — Codex Skills collection.
codexU is a native macOS menu bar and desktop application for monitoring OpenAI Codex / ChatGPT Codex and Claude Code usage. It provides real-time quota tracking (5-hour and 7-day windows), token usage analytics, project rankings, skill/tool usage stats, and a today task board. All data is read locally from ~/.codex/ and ~/.claude/ without uploading to third-party services.
What codexU Does
- Quota Monitoring: Displays 5-hour and 7-day quota remaining/used percentages with reset timers in the menu bar
- Token Analytics: Tracks uncached input, cached input, and output tokens with API cost equivalency estimates
- Task Board: Generates today's task board from local Codex threads and enabled automations
- Usage Trends: Shows 6-month daily token heatmap and 7-day trend summaries
- Project Rankings: Lists top projects by token usage, estimated value, thread count, and last activity
- Skill/Tool Stats: Displays most-used tools and skills from local session events
- Dual Runtime: Supports both Codex and Claude Code with global runtime switching
- Local-First: All data parsed from local SQLite and JSONL files, no external API calls for usage
Installation
From GitHub Release (Recommended)
-
Download the correct DMG for your Mac architecture from GitHub Releases:
- Apple Silicon:
codexU-<version>-mac-arm64.dmg
- Intel:
codexU-<version>-mac-x86_64.dmg
-
Open the DMG and drag codexU.app to /Applications
-
First launch requires manual security approval:
open /Applications/codexU.app
-
Grant file access when prompted:
- codexU needs read access to
~/.codex/ for Codex data
- If using Claude Code tracking, also needs
~/.claude/ access
From Source
Requirements:
- macOS 14+
- Xcode Command Line Tools
- Codex installed and logged in
git clone https://github.com/shanggqm/codexU.git
cd codexU
make build
make run
make install
make probe
Key Commands and Usage
Menu Bar Interactions
Keyboard Shortcuts
Command + U: Toggle main window visibility (customizable in Settings)
- Esc (during shortcut recording): Cancel
- Backspace (during shortcut recording): Clear
Main Window Views
- Today Tasks: In-progress, pending, scheduled, and completed tasks from local threads
- Usage Trends: Daily token heatmap (6 months) + 7-day trend summary
- Project Rankings: Top projects by token, value, threads, last active
- Skill Usage: Top tool calls and skill usage from local sessions
Settings Window
Access via menu bar Runtime menu → "Open Settings" or codexU app menu:
- General: Language (中文/English), appearance (auto/light/dark)
- Status Bar: Display mode (minimal/classic/rich), quota metric (used/remaining), visible indicators (5h/7d/today tokens/reset countdown)
- Window: Keep main window on top, close behavior (hide/quit)
- System: Auto-update check, check for beta versions, view status, manual update check
Configuration and Data Sources
Data Sources
codexU reads from local files only:
Codex:
~/.codex/state_5.sqlite # Account, quotas, token totals
~/.codex/sessions/**/rollout-*.jsonl # Fine-grained token events
~/.codex/archived_sessions/*.jsonl # Archived session events
~/.codex/automations/**/automation.toml # Enabled automations
Claude Code:
~/.claude/projects/**/*.jsonl # Transcript usage data
~/.claude/tasks/**/*.json # Task definitions
~/Library/Caches/codexU/claude-code/statusline-snapshot.json # Optional quota cache
Status Bar Customization
The status bar supports three display modes with adaptive single/dual ring layouts:
Minimal Mode:
- Bold quota ring only
- Adapts to single ring when only one quota window active
Classic Mode:
- Quota progress rings with percentage inside
- Separate ring per active quota window
Rich Mode:
- Full labels, progress bars, reset timers
- 5h/7d progress colors match main window blue/purple rings
Quota Metrics:
- Used: Clockwise/left-to-right progress
- Remaining: Counter-clockwise/right-to-left progress
API Cost Equivalency ("羊毛进度")
codexU estimates API-equivalent value for monthly Codex usage:
API Equivalent Value =
(uncached_input_tokens / 1,000,000) * uncached_input_price
+ (cached_input_tokens / 1,000,000) * cached_input_price
+ (output_tokens / 1,000,000) * output_price
Progress bar shows position relative to Plus, Pro 100, Pro 200, and max monthly value (~$46,500 based on 200M tokens/day * 30 days).
Code Examples
Building and Packaging
make build
make release
make release-arm64
make release-intel
make release-all
Accessing Local Usage Data (Swift)
codexU uses SwiftUI and Swift concurrency to parse local Codex data:
import Foundation
import SQLite3
func readCodexQuota() throws -> (fiveHour: Double, sevenDay: Double) {
let dbPath = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(".codex/state_5.sqlite")
.path
var db: OpaquePointer?
guard sqlite3_open(dbPath, &db) == SQLITE_OK else {
throw NSError(domain: "codexU", code: 1)
}
defer { sqlite3_close(db) }
var stmt: OpaquePointer?
let query = """
SELECT window_duration_seconds, remaining_count, total_count
FROM rate_limits
WHERE window_duration_seconds IN (18000, 604800)
"""
var quotas: [Int: (Double, Double)] = [:]
if sqlite3_prepare_v2(db, query, -1, &stmt, nil) == SQLITE_OK {
while sqlite3_step(stmt) == SQLITE_ROW {
let window = (sqlite3_column_int(stmt, ))
remaining (sqlite3_column_int64(stmt, ))
total (sqlite3_column_int64(stmt, ))
quotas[window] (remaining, total)
}
}
sqlite3_finalize(stmt)
fiveHour quotas[].map { . . }
sevenDay quotas[].map { . . }
(fiveHour, sevenDay)
}
Reading Session Token Events
import Foundation
struct TokenEvent: Codable {
let type: String
let timestamp: Date
let tokenCount: TokenCount
struct TokenCount: Codable {
let inputTokens: Int
let outputTokens: Int
let cacheReadInputTokens: Int?
let cacheCreationInputTokens: Int?
}
}
func parseSessionTokens(sessionPath: String) throws -> [TokenEvent] {
let rolloutPath = sessionPath + "/rollout-0.jsonl"
let content = try String(contentsOfFile: rolloutPath)
return content.split(separator: "\n")
.compactMap { line in
guard let data = line.data(using: .utf8),
let event = try? JSONDecoder().decode(TokenEvent.self, from: data),
event.type == {
}
event
}
}
(: ., : ) -> {
uncachedInput tokens.inputTokens (tokens.cacheReadInputTokens )
cachedInput tokens.cacheReadInputTokens
output tokens.outputTokens
uncachedInputPrice
cachedInputPrice
outputPrice
((uncachedInput) uncachedInputPrice)
((cachedInput) cachedInputPrice)
((output) outputPrice)
}
Custom Shortcut Registration
import Carbon
import SwiftUI
class ShortcutManager: ObservableObject {
@Published var currentShortcut: (modifiers: UInt32, keyCode: UInt16)?
private var eventHandler: EventHandlerRef?
func register(modifiers: UInt32, keyCode: UInt16, handler: @escaping () -> Void) {
guard modifiers.nonzeroBitCount >= 2,
(modifiers & UInt32(cmdKey)) != 0 || (modifiers & UInt32(controlKey)) != 0 else {
return
}
let hotKeyID = EventHotKeyID(signature: FourCharCode("codU"), id: 1)
var hotKeyRef: EventHotKeyRef?
RegisterEventHotKey(
keyCode,
modifiers,
hotKeyID,
GetApplicationEventTarget(),
0,
&hotKeyRef
)
currentShortcut (modifiers, keyCode)
}
}
Common Patterns
Setting Up First-Time Use
func checkCodexSetup() -> Bool {
let stateDB = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(".codex/state_5.sqlite")
guard FileManager.default.fileExists(atPath: stateDB.path) else {
print("Codex not installed or not used yet")
return false
}
let sessionsDir = FileManager.default.homeDirectoryForCurrentUser
.appendingPathComponent(".codex/sessions")
let hasSession = (try? FileManager.default.contentsOfDirectory(atPath: sessionsDir.path))?.isEmpty == false
return hasSession
}
Switching Between Codex and Claude Code
enum Runtime: String, CaseIterable {
case codex = "Codex"
case claude = "Claude Code"
}
@Published var selectedRuntime: Runtime = .codex {
didSet {
Task {
await refreshQuota()
await refreshTokenStats()
await refreshTaskBoard()
}
}
}
func toggleRuntime() {
selectedRuntime = selectedRuntime == .codex ? .claude : .codex
}
Adaptive Single/Dual Ring Layout
struct QuotaDisplay: View {
let fiveHourQuota: Double?
let sevenDayQuota: Double?
var body: some View {
HStack(spacing: 8) {
if let fiveHour = fiveHourQuota {
QuotaRing(percentage: fiveHour, label: "5h", color: .blue)
}
if let sevenDay = sevenDayQuota {
QuotaRing(percentage: sevenDay, label: "7d", color: .purple)
}
}
.animation(.easeInOut, value: fiveHourQuota != nil)
.animation(.easeInOut, value: sevenDayQuota != nil)
}
}
Particle Effects with Performance Optimization
struct ParticleView: View {
@Environment(\.scenePhase) private var scenePhase
@State private var isWindowFocused = false
let powerSavingMode: Bool
var shouldRenderParticles: Bool {
scenePhase == .active && isWindowFocused && !powerSavingMode
}
var body: some View {
ZStack {
if shouldRenderParticles {
ParticleEmitter()
.transition(.opacity)
}
}
.onReceive(NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification)) { _ in
isWindowFocused = true
}
.onReceive(NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification)) { _ in
isWindowFocused = false
}
}
}
Troubleshooting
Quota Shows -- or Empty
Problem: Status bar or main window shows -- for quotas instead of percentages.
Solutions:
- Verify Codex is logged in:
codex auth login
- Check SQLite file exists:
ls -la ~/.codex/state_5.sqlite
- Use at least once to generate state: Create a new Codex thread
- For Claude Code: Ensure statusline snapshot cache exists (optional feature)
- Check file permissions:
chmod 600 ~/.codex/state_5.sqlite
Token Statistics Not Updating
Problem: Today's tokens or trend charts show zero or stale data.
Solutions:
- Verify session files exist:
ls ~/.codex/sessions/
- Check rollout JSONL files:
find ~/.codex/sessions -name "rollout-*.jsonl"
- For Claude Code: Check transcript JSONL:
find ~/.claude/projects -name "*.jsonl"
- Click refresh button in main window
- Check Console.app for codexU parsing errors
App Won't Open After Install
Problem: macOS blocks app launch with security warning.
Solutions:
xattr -dr com.apple.quarantine /Applications/codexU.app
Shortcut Key Not Working
Problem: Custom shortcut doesn't trigger main window.
Solutions:
- Ensure at least 2 modifiers including Command or Control
- Check for conflicts: System Settings > Keyboard > Keyboard Shortcuts
- Reset to default in codexU Settings
- Common conflicts: Avoid
Command + Shift + U (Character Viewer)
- Try alternative:
Command + Control + U, Command + Option + U
High CPU Usage
Problem: codexU uses excessive CPU in background.
Solutions:
- Update to v1.0.5+ (optimized polling and particle rendering)
- Enable power saving mode in Settings (only renders particles on hover)
- Particle effects auto-disable when:
- Window is minimized or hidden
- Battery is low
- Thermal state is critical
- "Reduce motion" accessibility setting is on
- Close main window (hides Dock icon, keeps menu bar)
Build Errors from Source
Problem: make build fails with Swift compilation errors.
Solutions:
xcode-select --install
sw_vers
make clean
rm -rf build/
swift --version
make build ARCH=arm64
DMG Packaging Fails
Problem: make release fails or produces corrupt DMG.
Solutions:
brew install create-dmg
df -h
rm -rf dist/
mkdir -p dist/
make build
create-dmg \
--volname "codexU" \
--window-pos 200 120 \
--window-size 600 400 \
--icon-size 100 \
--app-drop-link 450 200 \
dist/codexU-test.dmg \
build/Release/codexU.app
Claude Code Quota Missing
Problem: Claude Code 5h/7d quotas show -- even with active subscription.
Cause: codexU needs optional statusline snapshot cache file.
Solutions:
- This is expected behavior (cache file is optional)
- Token statistics and trends still work from transcript JSONL
- Manual cache population not currently supported
- Only affects quota display, not usage analytics
Update Check Fails
Problem: "Check for Updates" in Settings shows connection error.
Solutions:
curl -I https://api.github.com/repos/shanggqm/codexU/releases/latest
File Access Denied Errors
Problem: codexU logs show permission denied for ~/.codex/ or ~/.claude/.
Solutions:
- Grant Full Disk Access: System Settings > Privacy & Security > Full Disk Access → Add codexU
- Or manually fix permissions:
chmod -R 755 ~/.codex
chmod -R 755 ~/.claude
- If using symlinks, ensure target directories are readable
- Check Console.app for specific file paths being denied
Reference Links