A macOS desktop pet that reacts to AI usage โ quota, token burn, cost, and work rhythm.
Your AI usage becomes a living companion โ no dashboards to open, no commands to run.
English ยท ็น้ซไธญๆ
/\_/\ tokens today โโโโโโโโโโ 68%
( o.o ) burn rate steady ยท mood: focused
> ^ < next reset in 2h 14m
Implemented and running. A SwiftUI/AppKit menu-bar app with a floating pixel pet, built from scratch. The repo contains the working app (SwiftPM, Sources/) plus the docs that matter most: exactly which local files are read and why (docs/DATA_SOURCES.md) and where the project is headed (ROADMAP.md).
- ๐ Native macOS โ SwiftUI + AppKit menu-bar app with a floating pixel-pet panel.
- ๐ Three pages, not one crowded dashboard โ Today, Limits, and Projects, plus a Trends view with usage heatmap and streaks.
- ๐ Provider adapters โ Codex and Claude Code (with official 5h/weekly limits); Grok Code enabled by default (token usage + plan tier; this app does not yet ingest Grok's official limits); Antigravity behind a research gate; OpenCode planned next.
- ๐งฎ Local ledger + limit engine โ quota, reset windows, token burn rate, and per-model costs from a pricing registry that is sourced & dated per entry.
- ๐ Feeding/XP loop and mood engine โ the pet reacts to signals such as quota remaining, burn rate, stale data, focus sessions, and usage milestones.
- ๐ Offline HTML report export โ a local, offline-readable snapshot of Today, Limits, Projects, pricing assumptions, and data-quality notes.
- ๐ Live updates & scheduled exports โ FSEvents file-watching keeps data fresh; report export can run on a schedule.
- ๐ชถ Monitor-only (low RAM) mode โ Settings โ General: the floating pet window and animations are never created, and the feeding/XP engine is only instantiated in full-pet mode (switching to monitor-only releases it); usage tracking, menu bar, pages, notifications, and export keep working.
- โจ๏ธ Headless
aipetCLI โ status, reports, and sprite export from the terminal.
Homebrew (Apple Silicon) โ recommended:
brew install --cask F-e-u-e-r/tap/ai-pet-usageThis handles install, brew upgrade, and brew uninstall. Or grab the latest AI-Pet-Usage-โฆ-arm64.zip from Releases and drag the app to Applications.
Build from source (about a minute; required for Intel Macs):
git clone https://github.com/F-e-u-e-r/ai-pet-usage.git
cd ai-pet-usage
Scripts/build-app.sh
open "dist/AI Pet Usage.app"- Requirements: macOS 14+. The Homebrew cask and the prebuilt zip are Apple Silicon; building from source needs the Xcode Command Line Tools (
xcode-select --install). - First launch: the alpha is ad-hoc signed and not notarized, so macOS blocks it the first time. Try to open the app, then go to System Settings โ Privacy & Security and choose Open Anyway (only if you trust the release). Homebrew does not remove this one-time approval โ only Developer ID notarization would (planned for the beta).
- The app lives in the menu bar; enable launch at login in Settings if you want it always on. It can check GitHub for updates (opt-in โ Settings โ General โ Automatically check for updates; a version check only, no usage data is sent), or check on demand from the menu bar.
- Developer IDโsigned/notarized downloads are planned for the beta (see
ROADMAP.md).
Claude Code pipes its official rate limits (the real 5-hour / weekly used_percentage) into whatever statusLine command you configure. If a hook saves that payload locally, the app shows provider-reported limits โ no manual token budget needed.
Easiest: one command. The aipet CLI ships inside the app bundle and installs everything (writes the bundled hook, backs up settings.json first, and โ if your statusLine already points at a script file โ wraps that script untouched):
"/Applications/AI Pet Usage.app/Contents/MacOS/aipet" install-hook # Homebrew / zip install
.build/debug/aipet install-hook # built from sourceAdd --dry-run to preview without writing. If your existing statusLine is a compound command (with pipes/arguments) rather than a single script path, it won't guess โ it refuses with guidance and leaves everything unchanged, so you can wrap it manually (see below). It also refuses (changing nothing) on symlinked/dotfiles-managed settings, non-command statusLine types, and unmanaged hook references, and prints the revert line after installing.
Manual alternative โ the same hook lives in the repo at Scripts/claude-statusline-hook.sh (not in the app bundle โ Homebrew users: clone the repo or grab the file from the source zip of a release).
Fresh install (you don't have a custom statusline yet) โ add to ~/.claude/settings.json:
"statusLine": {"type": "command", "command": "/bin/bash /path/to/ai-pet-usage/Scripts/claude-statusline-hook.sh"}Already have a custom statusline? Use wrapper mode โ your script is never modified, and its stdin (the exact original JSON bytes), stdout, stderr, and exit code all pass through untouched:
- Open
~/.claude/settings.json, find your currentstatusLine.command, and save that string somewhere (rollback = paste it back). - Replace it with the hook wrapping your command:
"statusLine": {"type": "command", "command": "/bin/bash /path/to/ai-pet-usage/Scripts/claude-statusline-hook.sh --wrap /Users/you/.claude/statusline-command.sh"}- The wrap target must be executable; for a plain shell script without the executable bit, use
--wrap /bin/bash -- /path/to/script.sh. - Extra arguments go after
--, one per argument (--wrap /path/to/cmd -- --compact); compound shell command strings are not accepted. - What gets persisted is a frozen allowlist only โ this exact shape, nothing else (session ids, transcript paths, cwd, and any unknown fields are dropped at every level):
{"schema_version": 1, "captured_at": "<UTC ISO8601>",
"model": {"id": "...", "display_name": "..."},
"rate_limits": {"five_hour": {"used_percentage": 42, "resets_at": 1789000000},
"seven_day": {"used_percentage": 81, "resets_at": 1789400000}}}- The hook makes no network requests; the file stays under
~/Library/Application Support/AIPetUsage/. A payload without usablerate_limitsnever overwrites the last good file; staleness is judged by the app from the file's mtime. - Alternative: any other tool that already saves the payload to
~/.claude/usage-status.jsonworks too โ then you don't need this hook at all.
Scripts/swiftpm.sh build # build everything
Scripts/swiftpm.sh run usagecore-tests # run the test suite
Scripts/build-app.sh # produce dist/AI Pet Usage.app
open "dist/AI Pet Usage.app"
.build/debug/aipet status # headless status (CLI)
.build/debug/aipet sprites # export pixel-pet contact sheets โ dist/sprite-preview/
.build/debug/aipet report --out r.html # headless HTML exportImportant
Always build through Scripts/swiftpm.sh, not bare swift build.
Why the wrapper exists (dev-machine CommandLineTools quirks)
This machine's CommandLineTools installation has two version-mismatch defects (a stale PackageDescription.private.swiftinterface and a duplicate SwiftBridging modulemap) that the wrapper works around per-invocation without touching system files. A CLT reinstall (sudo rm -rf /Library/Developer/CommandLineTools && xcode-select --install) makes the workarounds auto-disable. The CLT also lacks XCTest, so tests run as the usagecore-tests executable with an XCTest-compatible mini harness.
| Path | What lives there |
|---|---|
Sources/UsageCore |
Provider adapters, ledger, limit engine, pricing, HTML report โ no UI dependencies, so the parser logic can be reused by a CLI or Tauri build later. Model prices live in model-prices.json (sourced & dated per entry; see docs/DATA_SOURCES.md). |
Sources/PetCore |
Feeding/XP engine and mood engine โ consumes only normalized UsageCore state. |
Sources/AIPetUsage |
The macOS app: menu bar, floating pet panel, the three pages, settings. |
Sources/aipet |
Headless CLI for verification and scripting. |
Sources/usagecore-tests |
Test suite with synthetic fixtures. |
docs/DATA_SOURCES.md |
Exactly which local files are read, and why. |
- โ Reads only known local usage files or user-configured paths.
- ๐ซ Does not upload usage data. The optional update check (Settings โ General) contacts GitHub for the latest version only โ no usage data is sent, and it can be turned off.
- ๐ซ Does not require account credentials.
- ๐ฌ Every permission prompt explains why it is needed.
- ๐งฉ Provider adapters stay isolated, so data-source changes do not affect the pet engine.
See PRIVACY.md for the promise in plain language and docs/DATA_BOUNDARY.md for the per-data-class checklist of where each thing can (and can't) go.
The app and the aipet CLI share ~/Library/Application Support/AIPetUsage/. This is safe by design:
- CLI
status/reportare read-only by default โ they render the ledger and limit state already on disk (add--refreshto rescan provider logs). - Every writing phase (app refresh, CLI
--refresh,reindex) takes an exclusive interprocess file lock (refresh.lock, flock-based). A process that cannot get the lock within 60s skips the write, reports "refresh skipped" in data-quality notes, and serves cached data. - Before writing, each process converges with the other's progress (ledger reload on size change, per-file max-offset merge of scan state, limit-state reload). Event IDs are content-stable, so any overlap deduplicates instead of double-counting.
The app combines two ideas:
- A lightweight desktop pet with engagement mechanics.
- A local-first AI usage monitor for tools such as Codex, Claude Code, Antigravity, Grok Code, and later OpenCode.
The pet makes usage state visible without requiring the user to open dashboards or run commands, reacting to useful signals such as quota remaining, reset windows, token burn rate, stale data, focus sessions, and usage milestones.
The product deliberately avoids a crowded single-page dashboard: usage is separated into the three pages (Today, Limits, Projects), and the HTML report export is part of the alpha scope as a local, offline-readable snapshot of the same data.
macOS-first; Windows and Linux are planned as the next platform step after the macOS MVP is stable.
- SwiftUI + AppKit for the desktop shell, floating pet window, menu bar, notifications, and launch-at-login.
- A separate usage core with provider adapters, so the parser logic can later be reused by a Tauri or CLI version.
- Local JSON or SQLite storage for settings, achievements, and daily summaries.
Provider priority: v1 core โ Codex and Claude Code ยท shipped, limited data โ Grok Code (enabled by default; token usage + plan tier; this app does not yet ingest Grok's official limits) ยท research โ Antigravity, if a reliable local data source appears ยท v2 โ OpenCode.
| Document | Contents |
|---|---|
ROADMAP.md |
Phased delivery plan from product definition through distribution. |
PRIVACY.md ยท docs/DATA_BOUNDARY.md |
Privacy promise (plain language) and the per-data-class boundary checklist. |
docs/DATA_SOURCES.md |
Exact local files read by each provider adapter and the limit-calculation policy. |
docs/HTML_REPORT_EXPORT_SPEC.md |
Local static HTML report export requirements. |
Copyright (C) 2026 F-e-u-e-r
This project is licensed under the GNU AGPL-3.0-only. All code and pixel art (the dog, cat, and bird string-grid sprites) are original to this repository and carry the same license.
The app reads local data files produced by third-party tools (Claude Code, Codex, and Grok CLI session logs and status files). Reading them does not change their respective ownership or terms, and this project never redistributes their contents โ everything stays on your machine (see docs/DATA_SOURCES.md).