Skip to content
This repository was archived by the owner on Nov 16, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e97f225
Add Claude AI support with local log file analysis
steipete Jun 10, 2025
e73e166
Add o200k_base.tiktoken resource to project configuration
steipete Jun 10, 2025
4a53c1d
Fix Claude provider cookie handling in AuthenticationTokenManager
steipete Jun 10, 2025
21f2bbe
Add menu bar button highlight state when popover is shown
steipete Jun 10, 2025
1a1c86d
Add sandbox entitlement for Claude logs directory access
steipete Jun 10, 2025
a739c0b
Fix Claude provider UI not updating after granting folder access
steipete Jun 10, 2025
544b367
Remove unnecessary "Revoke Access" button for Claude provider
steipete Jun 10, 2025
7a1035a
Enable Claude provider and integrate with spending display
steipete Jun 10, 2025
7821f26
Remove orderFrontRegardless call from CustomMenuWindow
steipete Jun 10, 2025
bf752df
Add Claude token usage and cost breakdown to popover UI
steipete Jun 10, 2025
6ee36ba
Fix build errors and apply formatting/linting fixes
steipete Jun 10, 2025
c92b10c
Trigger login success flow when Claude folder access is granted
steipete Jun 10, 2025
3b60ea9
Add comprehensive logging for Claude integration debugging
steipete Jun 10, 2025
abab6cc
Fix Claude authentication flow and improve integration logging
steipete Jun 10, 2025
a01bd52
Improve onboarding flow by removing mandatory Cursor login requirement
steipete Jun 10, 2025
bcccb69
Fix Claude provider activation and add provider toggle controls
steipete Jun 10, 2025
a8d401b
Add Claude subscription tier configuration
steipete Jun 10, 2025
f2d815a
feat: Add official Claude app icon for provider tab
steipete Jun 10, 2025
3fe702e
feat: Add Claude subscription tier settings
steipete Jun 10, 2025
71478f8
fix: Improve Claude folder access validation and setup
steipete Jun 10, 2025
efb836c
feat: Improve provider configuration UI
steipete Jun 10, 2025
ed871bc
fix: Open settings to Providers tab when clicking Configure Providers
steipete Jun 10, 2025
d7a78a4
fix: Sync menu bar button highlight with popover visibility
steipete Jun 10, 2025
e6ac165
feat: Add automatic re-authentication for Cursor when cookies expire
steipete Jun 10, 2025
19393b2
fix: Improve Claude directory validation to handle sandboxed environm…
steipete Jun 10, 2025
cee507a
docs: Update CHANGELOG with Claude directory validation fix
steipete Jun 10, 2025
239d355
fix: Change mutable variable to constant in CoreBPE
steipete Jun 10, 2025
a0ff9aa
fix: Update Tuist configuration to apply Xcode recommended settings
steipete Jun 10, 2025
f725e57
fix: Remove 'No auth token found' error for Claude provider
steipete Jun 10, 2025
ecb2755
fix: Additional cleanup for Claude provider error handling
steipete Jun 10, 2025
ffb4603
feat: Add login consent flow for Cursor with credential disclosure
steipete Jun 10, 2025
e6c755a
docs: Update CHANGELOG with login consent flow feature
steipete Jun 10, 2025
531012e
fix: Correct AppDelegate property name in ClaudeLogManager
steipete Jun 10, 2025
f03137d
fix: Improve Claude log parsing to be more selective
steipete Jun 10, 2025
88d7677
Add Claude token usage report with navigation
steipete Jun 10, 2025
b39e07a
Optimize Claude log parsing with background processing and caching
steipete Jun 10, 2025
b0f3001
Fix ClaudeUsageReportView refresh task and update tests
steipete Jun 10, 2025
89257a6
Optimize Claude log parsing performance with multiple strategies
steipete Jun 10, 2025
fb64ac8
Implement persistent caching with SHA-256 hashing and background actor
steipete Jun 10, 2025
5ef4c51
Fix popover positioning fallback when menu bar icon is hidden
steipete Jun 10, 2025
97f4297
Fix FastScanner string index out of bounds crash
steipete Jun 10, 2025
d97310f
Optimize FastScanner performance with character array caching
steipete Jun 10, 2025
38915b4
Apply Swift formatter and linter updates
steipete Jun 10, 2025
2c66aff
Add utility extensions for common patterns
steipete Jun 10, 2025
d6644e7
Update services to use new utility extensions
steipete Jun 10, 2025
c5368bf
Update providers to use utility extensions
steipete Jun 10, 2025
972d882
Update UI components to use utility extensions
steipete Jun 10, 2025
0cc824b
Update tests to use utility extensions and new patterns
steipete Jun 10, 2025
a915979
Apply Swift formatter and fix trailing newlines
steipete Jun 10, 2025
229543b
Work on Claude Token Parsing
steipete Jun 10, 2025
091c320
Fixes token parsing
steipete Jun 11, 2025
4960277
Add documentation for token parsing fix
steipete Jun 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Codecov configuration for VibeMeter

coverage:
precision: 2
round: down
range: "60...90"

status:
project:
default:
target: 70%
threshold: 5%
base: auto

patch:
default:
target: 80%
threshold: 10%
base: auto

parsers:
xcode:
derived_data_path: ./build/DerivedData

ignore:
- "VibeMeterTests/**/*"
- "**/Mocks/**/*"
- "**/TestUtilities/**/*"
- "**/PreviewHelpers/**/*"
- "**/*.generated.swift"

comment:
layout: "reach,diff,flags,files,footer"
behavior: default
require_changes: true
104 changes: 104 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Code Coverage

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
coverage:
name: Generate Code Coverage
runs-on: macos-14

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Select Xcode
run: sudo xcode-select -s /Applications/Xcode_16.2.app

- name: Install Tuist
run: |
curl -Ls https://install.tuist.io | bash
echo "/usr/local/bin" >> $GITHUB_PATH

- name: Generate Xcode project
run: ./scripts/generate-xcproj.sh

- name: Build and test with coverage
run: |
xcodebuild test \
-workspace VibeMeter.xcworkspace \
-scheme VibeMeter \
-configuration Debug \
-enableCodeCoverage YES \
-resultBundlePath TestResults.xcresult \
-quiet

- name: Generate coverage report
run: |
# Extract coverage percentage
COVERAGE=$(xcrun xccov view --report --only-targets TestResults.xcresult | grep "VibeMeter.app" | awk '{print $3}')
echo "COVERAGE=$COVERAGE" >> $GITHUB_ENV

# Generate detailed report
xcrun xccov view --report TestResults.xcresult > coverage.txt

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./TestResults.xcresult
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false

- name: Create coverage badge
uses: schneegans/dynamic-badges-action@v1.7.0
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: YOUR_GIST_ID_HERE
filename: vibemeter-coverage.json
label: Coverage
message: ${{ env.COVERAGE }}
color: |
${{
startsWith(env.COVERAGE, '9') && 'brightgreen' ||
startsWith(env.COVERAGE, '8') && 'green' ||
startsWith(env.COVERAGE, '7') && 'yellowgreen' ||
startsWith(env.COVERAGE, '6') && 'yellow' ||
'red'
}}

- name: Comment PR with coverage
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const coverage = process.env.COVERAGE;
const body = `## 📊 Code Coverage Report

**Overall Coverage:** ${coverage}

<details>
<summary>View detailed report</summary>

\`\`\`
${require('fs').readFileSync('coverage.txt', 'utf8').slice(0, 3000)}
\`\`\`
</details>`;

github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body
})

- name: Upload coverage artifacts
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: |
TestResults.xcresult
coverage.txt
11 changes: 10 additions & 1 deletion .package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "c3e62e76f6d67b71f67795039de0b78d5ef197f40e098d26f0776f8c49530aad",
"originHash" : "369d1960bfd8c32fdcf0a65b3ea1773b1c5eba560caf3fd5c4976988315693e6",
"pins" : [
{
"identity" : "keychainaccess",
Expand Down Expand Up @@ -27,6 +27,15 @@
"revision" : "3d8596ed08bd13520157f0355e35caed215ffbfa",
"version" : "1.6.3"
}
},
{
"identity" : "viewinspector",
"kind" : "remoteSourceControl",
"location" : "https://github.com/nalexn/ViewInspector.git",
"state" : {
"revision" : "a6fcac8485bc8f57b2d2b55bb6d97138e8659e4b",
"version" : "0.10.2"
}
}
],
"version" : 3
Expand Down
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### 🚀 New Features
- **Claude AI Support** - Added comprehensive Claude usage tracking via local log file analysis
- **Dual-Mode Menu Bar Gauge** - Toggle between total spending and Claude 5-hour window quota display
- **5-Hour Window Tracking** - Real-time monitoring of Claude's rolling quota with visual progress bar
- **Token Counting** - Integrated Tiktoken library with o200k_base encoding for accurate token calculation
- **Daily Usage Breakdown** - New detailed view showing Claude token usage per day with cost calculation
- **No-Login Authentication** - Claude integration works without login, using secure folder access instead
- **Claude Subscription Tiers** - Added support for Free, Pro ($20), Max 5× ($100), and Max 20× ($200) tiers
- **Automatic Re-authentication** - Cursor sessions now automatically re-authenticate when cookies expire
- **Official Claude Icon** - Extracted and integrated the official Claude app icon for better recognition
- **Login Consent Flow** - New consent dialog explains credential handling before Cursor login

### 🎨 UI Improvements
- **ClaudeQuotaView** - Dedicated 5-hour window progress display in the popover
- **ClaudeDetailView** - Table view with daily token usage breakdown and costs
- **Gauge Representation Setting** - New toggle in settings to switch between spending/quota display
- **Claude Account Type Setting** - Select subscription tier for accurate quota calculations
- **Provider Configuration** - Login/Grant Access buttons now always visible with auto-enable on click
- **Settings Navigation** - Configure Providers button opens directly to Providers tab
- **Menu Bar Highlight** - Button highlight state now properly syncs with popover visibility
- **Improved Provider Dialog** - Larger dialog (600×700) with better layout and removed logout button

### 🔧 Technical Improvements
- **Refactored ClaudeLogManager** - Made testable with dependency injection and protocol-based design
- **Comprehensive Test Suite** - Added extensive tests for Claude provider functionality with mocks
- **Sandbox Security** - Implemented security-scoped bookmarks for safe folder access
- **Protocol-Based Architecture** - ClaudeLogManagerProtocol enables better testing and flexibility
- **Folder Access Validation** - Validates home directory selection and cleans up invalid bookmarks
- **Credential Storage** - Secure storage of Cursor credentials in macOS Keychain for auto-auth
- **CAPTCHA Detection** - Automatic detection and user notification when manual intervention needed
- **WebView Preloading** - Login page preloads in background while consent dialog is shown

### 🐛 Bug Fixes
- **Fixed Claude initialization race condition** - Claude now properly initializes before data refresh
- **Fixed provider error messages** - Shortened error messages to prevent UI truncation
- **Fixed directory picker** - Now pre-selects actual home directory instead of sandboxed path
- **Fixed Claude directory validation** - Properly handles sandboxed environments and accepts any directory containing .claude/projects

## [1.1.0] - 2025-06-10

### 🎨 UI Improvements
Expand Down
1 change: 1 addition & 0 deletions Derived/Sources/TuistAssets+VibeMeter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

public enum VibeMeterAsset: Sendable {
public static let accentColor = VibeMeterColors(name: "AccentColor")
public static let claude = VibeMeterImages(name: "claude")
public static let cursor = VibeMeterImages(name: "cursor")
}

Expand Down
8 changes: 8 additions & 0 deletions Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ let project = Project(
url: "https://github.com/kishikawakatsumi/KeychainAccess.git",
requirement: .upToNextMajor(from: "4.0.0")),
.remote(url: "https://github.com/sparkle-project/Sparkle.git", requirement: .upToNextMajor(from: "2.0.0")),
.remote(url: "https://github.com/nalexn/ViewInspector.git", requirement: .upToNextMajor(from: "0.9.0")),
],
settings: .settings(
base: [
Expand Down Expand Up @@ -52,6 +53,9 @@ let project = Project(
"GCC_WARN_UNUSED_VARIABLE": true,
"CLANG_WARN_UNREACHABLE_CODE": true,
"ENABLE_STRICT_OBJC_MSGSEND": true,
// Recommended settings from Xcode
"ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOL_EXTENSIONS": "YES",
"LOCALIZATION_PREFERS_STRING_CATALOGS": "YES",
],
configurations: [
.debug(
Expand All @@ -64,6 +68,8 @@ let project = Project(
// Less strict for development
"GCC_TREAT_WARNINGS_AS_ERRORS": false,
"SWIFT_TREAT_WARNINGS_AS_ERRORS": false,
// Enable code coverage
"ENABLE_CODE_COVERAGE": true,
],
xcconfig: nil),
.release(
Expand Down Expand Up @@ -108,6 +114,7 @@ let project = Project(
],
resources: [
.glob(pattern: "VibeMeter/Assets.xcassets", excluding: []),
.glob(pattern: "VibeMeter/Core/Utilities/Tiktoken/*.tiktoken", excluding: []),
],
entitlements: .file(path: "VibeMeter/VibeMeter.entitlements"),
dependencies: [
Expand Down Expand Up @@ -140,6 +147,7 @@ let project = Project(
sources: ["VibeMeterTests/**"],
dependencies: [
.target(name: "VibeMeter"),
.package(product: "ViewInspector"),
],
settings: .settings(
base: [
Expand Down
61 changes: 56 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ A beautiful, native macOS menu bar application that helps you track your monthly
## ✨ Features

- **📊 Real-time Spending Tracking** - Monitor your AI service costs directly from your menu bar
- **🔄 Multi-Provider Support** - Currently supports Cursor AI with extensible architecture for future services
- **🔄 Multi-Provider Support** - Supports Cursor AI and Claude with extensible architecture for future services
- **🎭 Claude Integration** - Track Claude usage via local log file analysis with 5-hour window quota monitoring
- **💰 Multi-Currency Support** - View spending in USD, EUR, GBP, JPY, and 20+ other currencies
- **🔔 Smart Notifications** - Customizable spending limit alerts to keep you on budget
- **🎨 Animated Gauge Display** - Beautiful visual indicator showing spending progress
Expand All @@ -27,26 +28,74 @@ A beautiful, native macOS menu bar application that helps you track your monthly
## 📋 Requirements

- **macOS 15.0** or later (Sequoia)
- **Cursor AI account** (free or paid)
- **Cursor AI account** (free or paid) and/or **Claude** (desktop or VS Code extension)
- **Internet connection** for real-time data sync

## 🎯 How It Works

Vibe Meter connects securely to your Cursor AI account and monitors your monthly usage:
Vibe Meter monitors your AI service usage through different methods:

### Cursor AI
- **Secure Login** - Connects via official web authentication
- **API Integration** - Fetches usage data directly from Cursor's servers
- **Automatic Sync** - Updates spending data every 5 minutes

### Claude
- **Local Log Analysis** - Reads usage data from `~/.claude/projects/` with your permission
- **No Login Required** - Select your account type (Free/Pro) in settings
- **5-Hour Window Tracking** - Monitors Claude Pro's rolling quota in real-time
- **Token Counting** - Uses OpenAI's o200k_base encoding for accurate calculations

### All Providers
- **Visual Indicators** - Gauge fills up as you approach your spending limits
- **Progress Notifications** - Alerts at 80% and 100% of your warning threshold
- **Currency Conversion** - Real-time exchange rates for accurate international tracking

### 📊 Gauge Behavior

The gauge icon in the menu bar adapts its display based on your usage:
The gauge icon in the menu bar has two display modes:

#### Total Spending Mode (Default)
- **No Money Spent** - When you haven't spent any money yet (but have used requests), the gauge shows the percentage of API requests used. For example, if you've used 3 out of 500 requests, the gauge shows 0.6% filled.
- **Money Spent** - Once you start spending money, the gauge switches to show spending as a percentage of your upper limit. For example, if you've spent $15 out of a $30 limit, the gauge shows 50% filled.

This intelligent behavior ensures the gauge always provides meaningful feedback about your usage, whether you're on a free tier using requests or actively spending on premium features.
#### Claude Quota Mode (Optional)
- **5-Hour Window** - For Claude Pro users, the gauge can show your remaining quota in the 5-hour rolling window
- **Real-time Updates** - The gauge updates as you use Claude, showing how much of your quota remains
- **Toggle in Settings** - Switch between spending and quota display modes in General Settings

This intelligent behavior ensures the gauge always provides meaningful feedback about your usage, whether you're tracking overall spending or Claude's specific quota limits.

## 🧪 Testing & Code Coverage

VibeMeter maintains comprehensive test coverage to ensure reliability:

### Running Tests
```bash
# Run all tests
xcodebuild test -workspace VibeMeter.xcworkspace -scheme VibeMeter

# Run tests with code coverage
./scripts/generate-coverage-report.sh

# Generate HTML coverage report and open it
./scripts/generate-coverage-report.sh --html --open

# Enforce minimum coverage threshold
./scripts/generate-coverage-report.sh --min-coverage 70
```

### Test Coverage
- **Current Coverage**: ~80-85%
- **Test Files**: 40+ test suites
- **Test Categories**:
- Unit tests for business logic
- Integration tests for provider workflows
- UI component tests (with ViewInspector)
- Performance benchmarks

### Continuous Integration
Code coverage is automatically generated and reported on all pull requests via GitHub Actions.

## ⚙️ Configuration

Expand All @@ -58,6 +107,8 @@ This intelligent behavior ensures the gauge always provides meaningful feedback
- **Show Cost in Menu Bar** - Toggle cost display next to the icon
- **Currency Selection** - Choose from 20+ supported currencies
- **Notification Preferences** - Customize alert frequency and triggers
- **Gauge Representation** - Choose between total spending or Claude quota display
- **Claude Account Type** - Select Free or Pro for accurate cost calculations

## 🛠️ Development

Expand Down
Loading
Loading