This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
OpenUsage is a terminal dashboard (TUI) for monitoring AI coding tool usage and spend. It auto-detects AI tools and API keys on the workstation and displays live data using Bubble Tea. Written in Go, requires CGO enabled (for mattn/go-sqlite3 used by the Cursor provider).
make build # build binary to ./bin/openusage (includes version ldflags)
make test # run all tests with -race and coverage
make test-verbose # verbose test output
make lint # golangci-lint (skips gracefully if not installed)
make fmt # go fmt ./...
make vet # go vet ./...
make run # go run cmd/openusage/main.go
make demo # build and run demo with dummy data (for screenshots)
make sync-tools # regenerate all AI tool configs from canonical template
# Run a single test
go test ./internal/providers/openai/ -run TestFetch -v
# Run provider tests only
go test ./internal/providers/...- Standard
gofmtwithgoimports. Tabs for indentation. - Import groups (separated by blank lines): stdlib, third-party, internal.
- Bubble Tea aliased as
tea. - Errors wrapped with provider prefix:
fmt.Errorf("openai: creating request: %w", err). - Pointer fields for optional numerics:
Limit *float64. - JSON tags use
snake_casewithomitemptyfor optional fields.
There are two runtime modes:
Direct mode (default):
main.go → config.Load() → runDashboard()
→ detect.AutoDetect() → registers providers from providers.AllProviders()
→ polls providers concurrently on a ticker
→ snapshots sent to TUI via tea.Program.Send(SnapshotsMsg)
Daemon mode (openusage telemetry):
daemon.Server polls providers → ingests into SQLite (telemetry.Store)
→ TUI connects via daemon.ViewRuntime over unix socket
→ daemon.ReadModel hydrates snapshots from stored events
→ telemetry events deduplicated, mapped to providers via ProviderLinks
Every provider implements core.UsageProvider (internal/core/provider.go):
type UsageProvider interface {
ID() string
Describe() ProviderInfo
Spec() ProviderSpec
DashboardWidget() DashboardWidget
DetailWidget() DetailWidget
Fetch(ctx context.Context, acct AccountConfig) (UsageSnapshot, error)
}ProviderSpec(provider_spec.go) bundles auth/setup metadata + widget definitions.DashboardWidget/DetailWidgetdefine how provider metrics render in the TUI.- Providers are registered in
internal/providers/registry.goviaAllProviders().
- HTTP header probing (
openai,anthropic,groq,mistral,deepseek,xai,gemini_api,alibaba_cloud): Lightweight API request, parse rate-limit headers using shared helpers frominternal/parsers/. - Rich API / local hybrid (
openrouter,cursor): Multiple API endpoints;cursoralso reads local SQLite DBs as fallback. - Local file readers (
claude_code,codex,gemini_cli,ollama): Read local stats/session files.claude_codeis the most complex with billing block computation and burn rate tracking. - CLI subprocess (
copilot): Shells out toghCLI commands. - Plugin/integration (
opencode): Reads local session data from the OpenCode tool.
Built with Bubble Tea's Model-Update-View pattern. Two screens cycled with Tab:
- Dashboard — tile grid (
tiles.go) with master-detail: left list + right detail panel (detail.go) - Analytics — spend analysis with sub-tabs (
analytics.go)
Theme system with 6 themes in styles.go, cycled with t. Visual components: smooth gauge bars (gauge.go), bar charts (charts.go), animated help overlay (help.go), fixed-size widget panels (widget.go), settings modal (settings_modal.go).
Provider widgets (provider_widget.go) are driven by DashboardWidget/DetailWidget definitions from each provider's Spec().
Background data collection system with server/client architecture:
daemon.Server— polls providers on interval, ingests snapshots into SQLitedaemon.ViewRuntime— client-side runtime that connects to daemon over unix sockettelemetry.Store— SQLite-backed event storage with deduplicationtelemetry.Pipeline— processes events from multiple sources (collector, hooks, spooling)telemetry.ReadModel— buildsUsageSnapshotviews from stored eventstelemetry.ProviderLinks— maps telemetry source systems to display provider IDs
Scans for installed tools (Cursor, Claude Code, Codex, Copilot, Gemini CLI, Aider, Ollama) and environment variables for API keys. Auto-detected accounts merge with manually configured ones; configured accounts take precedence.
/develop-feature <name> — Orchestrates the full lifecycle from idea to PR. Chains all skills below with user decision points between each phase. Start here for new features.
Full specification: docs/skills/develop-feature/SKILL.md
Use these directly when you need a specific phase, or let /develop-feature chain them:
| Command | Skill | Purpose |
|---|---|---|
/design-feature <name> |
SKILL.md | Design a feature: quiz, explore codebase, write design doc with tasks |
/review-design <name> |
SKILL.md | Validate design doc against codebase, fix discrepancies via quiz loop |
/implement-feature <name> |
SKILL.md | Execute design tasks with tests, parallel where possible |
/validate-feature <name> |
SKILL.md | Verify build, tests, design compliance, code quality |
/iterate-feature <name> |
SKILL.md | Triage and fix issues from validation or PR review |
/finalize-feature <name> |
SKILL.md | Create branch, commit, open PR with summary |
/add-new-provider <name> |
add-new-provider.md | Add a new AI provider (specialized 7-phase process) |
| Command | Skill | Purpose |
|---|---|---|
/cut-release |
SKILL.md | Tag, push, and publish a GitHub release with hand-crafted notes |
| Command | Skill | Purpose |
|---|---|---|
/dev-workflow-improvements |
SKILL.md | Audit dev workflow, sync tool configs, validate skill completeness |
/design-feature → /review-design → /implement-feature → /validate-feature → /iterate-feature → [docs sweep] → /finalize-feature
Each skill has a design doc in docs/skills/<name>/ and a slash command in .claude/commands/<name>.md.
Every PR that ships code is also a docs PR. Before opening or
re-pushing the PR you MUST audit user-facing docs under
docs/site/docs/ and update or create pages affected by the change.
This is enforced as Phase 0.5 of /finalize-feature and Phase 5.5
of /develop-feature. A PR that ships code without the matching docs
update gets bounced. If no docs change is genuinely needed, the PR
description must include a one-line justification.
The docs site lives at docs/site/. Build it with
DOCS_PREVIEW=1 npm run build from that directory; it must complete
with [SUCCESS] and no broken-link warnings.
- CGO is required due to
github.com/mattn/go-sqlite3(Cursor provider + telemetry store). This affects cross-compilation. AccountConfig.Tokenhasjson:"-"— never persisted to config. Providers that need runtime tokens must extract them inFetch().AccountConfig.BinaryandAccountConfig.BaseURLare repurposed for non-API providers (e.g., Binary stores file paths forclaude_code).- Config file:
~/.config/openusage/settings.json. Reference config:configs/example_settings.json. - Debug logging: set
OPENUSAGE_DEBUG=1. - API keys are referenced via
api_key_envin config (env var name), never stored directly. - CLI uses cobra (
cmd/openusage/main.go): default command runs dashboard,telemetrysubcommand runs daemon.
- Standard
testingpackage, no mocking frameworks. - Provider tests use
httptest.NewServerwith controlled headers/responses. - Table-driven tests for type logic (see
core/types_test.go). - Config tests use
t.TempDir()for temp files. - Telemetry tests use in-memory SQLite stores.
Follow /add-new-provider <name>. See the Skills section above for details.