Architecture
Tokfuel is a SwiftUI menu-bar app with no server component. Every number it shows comes from parsing files that Claude Code, Cursor, and Codex CLI already keep on your Mac.
SPM layers
App code under App/ is split into SPM targets. Dependencies flow UI → Store → sources; the Tokfuel executable wires the concrete types.
TokfuelUI— popover, Settings, About, and other presentationTokfuelStore— aggregation and UI-facing shaping (UsageStore)TokfuelClaude/TokfuelCursor/TokfuelCodex/TokfuelBudget— fetch and domain sourcesTokfuelSettings/TokfuelAnalytics/TokfuelCore— settings, opt-in Analytics, shared types
Data flows fetch → shape / aggregate → present. The decision record is ADR-0002; the repository README has the same dependency diagram.
UsageStore: the single source of truth
UsageStore (in TokfuelStore) is the one @MainActor object every view reads from. It owns the current cost totals, charts, and per-model breakdown, and refreshes them on a timer — every 10 minutes when idle, stepping up to once a minute for 5 minutes after cost moves. PopoverView and the rest of the UI stay pure display layers; settings live separately in AppSettings, backed by UserDefaults.
RetokService: reading Claude Code's own transcripts
RetokService (in TokfuelClaude) shells out to the bundled retok script (© Daiki Matsudate, MIT, kept unmodified) to parse the ~/.claude/projects/ transcripts Claude Code writes on its own — no hooks, no extra install, nothing to configure. python3 is an optional dependency; without it, cost analysis degrades but the rest of the app keeps working.
BudgetMonitor
BudgetMonitor (in TokfuelBudget) watches UsageStore's totals against the monthly and daily limits in AppSettings, and fires a heads-up exactly once each time a threshold is crossed. The alert window itself is assembled by the App / UI side.
Per-source services
CursorDashboardService— when Cursor is installed and signed in, calls Cursor's own dashboard usage API using the session token Cursor already keeps in its local state.vscdb. Falls back to a local SQLite token snapshot when signed out or offline.CursorPricingService— fetches Cursor's own published price table once a day to price fallback-path usage. Never guesses a rate for an unpriced model; it's counted as $0 instead.ExchangeRateService— fetches the USD→JPY rate from Frankfurter once a day, only when JPY display is enabled.UpdateChecker— polls GitHub Releases for a newer version at launch and then every 24 hours; downloads only when you click Update.CSVExportService— turns the current period's data already in the retok report into daily or monthly CSV, entirely on-device.AnalyticsService— the single surface for opt-in, distribution-build-only Firebase Analytics events; a no-op everywhere else.
Each source is kept independent and labeled — Cursor and Codex costs are never silently merged into the Claude total.
The App/ tree
Per ADR-0001, app-related trees live under App/. Site, Docs, and Scripts stay outside. Verification artifacts sit in App/Tests/ beside the SPM layer packages (App/Tokfuel*).
App/Tokfuel*— executable and libraries (UI / Store / sources)App/Tests/UnitTests— whatswift testrunsApp/Tests/{IntegrationTests,E2E,TestDocs}— integration, end-to-end, and scenario docs (see Testing)