Monitor Claude Code usage costs from local logs with threshold alerts.
Key Features • Installation • Usage • Configuration • Keybindings
Read Claude Code JSONL session logs from your local data directories, recursively scanning all projects and extracting usage from message.usage.
Data roots are discovered in this order:
CLAUDE_CONFIG_DIR(comma-separated directories, highest priority)claude_data_dirfrom config- auto-discovered defaults (
~/.config/claude/projects,~/.claude/projects)
Note: When CLAUDE_CONFIG_DIR is set, it overrides all other roots and must resolve to at least one existing projects/ directory. If none of the configured directories contain projects/, cceye errors instead of falling back to claude_data_dir or auto-discovered defaults.
Choose how costs are computed:
auto: usecostUSDfrom log when present, otherwise calculate from token pricingcalculate: always calculate from token countsdisplay: always usecostUSDfrom log (fallback to0)
Evaluate spend against daily, weekly, and monthly thresholds with warning and critical levels.
Send alerts to:
- Console output
- macOS Notification Center
- Slack webhook
- SMTP email
Track current costs, hourly trend, model/project breakdown, and notification history in a keyboard-driven terminal dashboard.
Generate report snapshots with:
dailyweeklymonthlysession
Each report supports date filters and JSON output options.
Pricing is fetched from LiteLLM and cached at ~/.config/cceye/pricing-cache.json (24h TTL), with fallback prices for known models.
Install/uninstall a LaunchAgent for background monitoring with log files under ~/Library/Logs/cceye/.
- Node.js 20 or later
- Claude Code logs available locally (e.g.
~/.claude/projects)
npm install -g cceyenpx cceye statusgit clone https://github.com/ydah/cceye.git
cd cceye
npm install
npm run buildnpm run typecheck
npm run lintIf you installed globally, use cceye. If not, replace it with npx cceye.
- Generate config interactively:
cceye init-
Edit thresholds and notification settings in
~/.config/cceye/config.yaml. -
Run a one-shot status check:
cceye status- Start daemon mode (foreground, silent):
cceye- Start daemon mode with logs (debug):
cceye -d- Start dashboard mode:
cceye dashboardcceye [command] [--config /path/to/config.yaml]
Commands:
--version Print CLI version
-v Print CLI version
(none) Start daemon mode (silent)
debug Start daemon mode with logs
--debug Enable debug logs for daemon mode
-d Alias for --debug
dashboard Start TUI dashboard mode
status Run one poll cycle and print current totals (no notifications)
daily Print daily usage report
weekly Print weekly usage report
monthly Print monthly usage report
session Print session usage report
init Interactive config generator
install Install macOS LaunchAgent
uninstall Remove macOS LaunchAgent
- Any command accepts
--config <path>. - Report commands support:
--since YYYYMMDD--until YYYYMMDD--json--breakdown--timezone <IANA TZ>--offline
- If not installed globally, run commands with
npx cceye <command>. - Starting with no arguments (
cceye) clears notification cooldown flags once before daemon startup.
Default path:
~/.config/cceye/config.yaml
See config.example.yaml for a complete template.
| Field | Description |
|---|---|
claude_data_dir |
Fallback root directory containing Claude JSONL logs (~ expansion supported) |
polling_interval_milliseconds |
Polling interval in milliseconds (>= 1) |
timezone |
Timezone used for daily/weekly/monthly window boundaries |
cost_mode |
auto, calculate, or display |
thresholds.* |
Warning/Critical cost thresholds per window |
notifications.console.enabled |
Enable console alerts |
notifications.macos.enabled |
Enable macOS notifications |
notifications.macos.sound |
Play notification sound on macOS |
notifications.slack.enabled |
Enable Slack alerts |
notifications.slack.webhook_url |
Slack Incoming Webhook URL |
notifications.slack.mention |
Optional mention prefix (<!channel>, <@U...>) |
notifications.email.enabled |
Enable SMTP email alerts |
notifications.email.* |
SMTP sender/recipient/auth settings |
notification_cooldown_minutes |
Cooldown for repeated alerts of same window/level |
log_level |
debug, info, warn, error |
dashboard.refresh_interval_seconds |
Required setting (currently not used by runtime logic) |
warningmust be less thancriticalfor all windows.- If Slack is enabled,
webhook_urlis required. - If email is enabled, these are required:
smtp_host,smtp_port,smtp_user,smtp_pass,from,to
| Variable | Description |
|---|---|
CLAUDE_CONFIG_DIR |
Comma-separated Claude config roots; each must contain projects/ |
CCEYE_SLACK_WEBHOOK_URL |
Fills Slack webhook when Slack is enabled and URL is missing in config |
CCEYE_SMTP_PASS |
Fills SMTP password when email is enabled and password is missing in config |
| Key | Action |
|---|---|
q / Ctrl-C |
Quit dashboard |
r |
Trigger refresh immediately |
d |
Switch breakdown window to daily (current target) |
w |
Switch breakdown window to weekly (current target) |
m |
Switch breakdown window to monthly (current target) |
p |
Toggle breakdown target (model / project) |
Tab |
Move focus to next panel |
↑ / ↓ |
Scroll notification log |
| File | Purpose |
|---|---|
~/.config/cceye/config.yaml |
Runtime configuration |
~/.config/cceye/state.json |
Notification state, cooldown markers, and internal state |
~/.config/cceye/data.json |
Dashboard-facing current aggregates and history |
~/.config/cceye/pricing-cache.json |
Cached model pricing data |
This is the most reliable way to keep the daemon running in the background on macOS, including after login.
cceye install --config ~/.config/cceye/config.yamlcceye uninstall --config ~/.config/cceye/config.yaml~/Library/Logs/cceye/stdout.log~/Library/Logs/cceye/stderr.log
Use this when you want a simple background process without installing LaunchAgent.
mkdir -p ~/.local/state/cceye
nohup cceye --config ~/.config/cceye/config.yaml > ~/.local/state/cceye/daemon.log 2>&1 &
echo $! > ~/.local/state/cceye/daemon.pidkill "$(cat ~/.local/state/cceye/daemon.pid)"
rm -f ~/.local/state/cceye/daemon.pidCreate ~/.config/cceye/config.yaml or pass --config explicitly.
Verify the following, depending on your environment:
- If
CLAUDE_CONFIG_DIRis set: ensure its paths exist and contain Claude session logs, or unset it. - Otherwise, ensure either:
claude_data_dirpoints to Claude session logs, or- one default path exists and contains logs:
~/.config/claude/projects~/.claude/projects
When a channel is enabled, all required fields for that channel must be present and valid.
When running from source, rebuild before running to ensure dist/ is up to date:
npm run buildnpm run dev
npm test
npm run test:coverage
npm run build- Existing config files remain valid.
- Path resolution is now more flexible:
- If
CLAUDE_CONFIG_DIRis set, those paths are used exclusively. - Otherwise,
claude_data_diris used, with auto-discovered defaults appended when available.
- If
- New report commands are available:
cceye dailycceye weeklycceye monthlycceye session