Add Claude AI usage tracking with local log file parsing#32
Conversation
- Integrated Tiktoken library with o200k_base encoding for accurate token counting - Created ClaudeLogManager with security-scoped bookmarks for sandbox file access - Implemented ClaudeProvider conforming to ProviderProtocol - Added dual-mode menu bar gauge (total spending vs Claude 5-hour quota) - Created ClaudeQuotaView for 5-hour window progress display - Added ClaudeDetailView for daily usage breakdown - Updated ProvidersSettingsView to handle Claude's folder access - Added comprehensive tests with dependency injection support - Updated documentation (spec.md, CHANGELOG.md, README.md) This implementation reads usage data from ~/.claude/projects/ without requiring login, tracks Claude Pro's 5-hour rolling quota, and integrates seamlessly with VibeMeter's multi-provider architecture.
Claude uses local file access instead of cookies for authentication. Updated getCookies() to return nil for providers without cookie support and fixed the corresponding test to expect this behavior. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Implements proper visual feedback by highlighting the status bar button when the custom menu window is visible. The button now shows an active state (similar to native macOS behavior) while the dropdown is open. - Added onHide callback to CustomMenuWindow for cleanup actions - Updated StatusBarMenuManager to control button highlight state - Button highlights when window shows, unhighlights when it hides - Works correctly for both manual dismissal and click-outside dismissal 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
❌ Build or Tests Failed!Version: 1.2 (1) This PR ran quick tests. Full test suite runs on merge to main. |
Added com.apple.security.temporary-exception.files.home-relative-path.read-only entitlement to allow read-only access to ~/Library/Logs/Claude/ directory. This fixes the sandbox error when ClaudeLogManager attempts to read Claude AI usage logs. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Added com.apple.security.files.user-selected.read-only entitlement to fix "Unable to display open panel" error - Made ProviderRowView properly observe ClaudeLogManager changes by using @StateObject instead of accessing the shared instance directly - This ensures the UI updates immediately when folder access is granted 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
The revoke access functionality is not needed for the Claude provider since users can manage folder permissions through system settings. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Enabled Claude provider by default alongside Cursor in ProviderRegistry - Added authentication token management to ClaudeLogManager to properly integrate with the orchestrator's login flow - When folder access is granted, a dummy token is saved to indicate Claude is "logged in" so it appears in the spending data - When access is revoked or not present, the token is removed This allows Claude spending data to appear in the main popover UI alongside Cursor spending, with proper cost calculations based on token usage. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This call was causing potential display issues and is not needed since NSApp.activate already handles window ordering properly. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Modified CostTableView to show Claude-specific token usage information - Added detailed token counts with cost breakdown in pricing description - Shows input/output tokens separately with their individual costs - Displays 5-hour window usage for Pro accounts with progress bar - Formats token counts with thousands separators for readability Now users can see: - Total input tokens and their cost - Total output tokens and their cost - 5-hour window message usage (for Pro tier) - All formatted nicely in the main popover alongside Cursor spending 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fixed ClaudeProvider to use ProviderPricingDescription type correctly - Fixed CostTableView to use correct property name (latestInvoiceResponse) - Applied SwiftFormat fixes for trailing spaces, line wrapping, and formatting - Fixed line length violation in ClaudeLogManager - All tests pass, build succeeds, linters show only minor warnings 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Call handleAuthenticationResponse after successful folder access grant - This ensures Claude data is fetched and displayed in the popover - The orchestrator will now refresh Claude data when access is granted 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Added detailed logging throughout the Claude data flow: - ClaudeProvider: Log all data fetching steps, token counts, and costs - ClaudeLogManager: Log file discovery, parsing progress, and errors - MultiProviderDataOrchestrator: Log Claude-specific initialization and refresh - BackgroundDataProcessor: Log Claude data processing steps This will help debug issues with Claude not appearing in the popover by showing: - Whether the token is found on startup - Which log files are discovered and parsed - How many entries are processed - What costs are calculated - Whether the data makes it through the orchestrator To debug, check Console.app for messages containing "Claude:" or "ClaudeLogManager:" 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Ensures Claude provider properly integrates with the login system when folder access is granted, completing the multi-provider architecture support for local AI usage tracking. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Previously, users were forced to log in to Cursor before they could use the app, even if they only wanted to use Claude. This change replaces the blocking login flow with a friendly configuration view that lets users choose which AI service provider to set up first. Changes: - Add NoProvidersConfiguredView with welcome message and setup guidance - Update VibeMeterMainView to show configuration view instead of login - Enable direct navigation to providers settings tab for easy setup - Remove requirement to log in to Cursor before using the app This improves the first-run experience by making the app more flexible and accessible to users who may only want to track Claude costs. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This fixes an issue where Claude's spending calculations weren't being triggered because the provider wasn't automatically enabled in the ProviderRegistry when folder access was granted. Changes: - ClaudeLogManager now enables Claude provider when folder access is available - Added toggle switches to ProviderRowView for enabling/disabling providers - Claude provider is enabled automatically when folder access is granted - Visual styling grays out disabled providers for better UX This ensures that Claude calculations work immediately after granting access and gives users control over which providers are active. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Enable users to select their Claude subscription tier (Free, Pro, or Team) for more accurate usage calculations based on their actual plan limits. - Enhanced ClaudePricingTier enum with pricing and limit details - Free: 50 messages/day, resets at midnight PT - Pro ($20/mo): ~45 messages per 5-hour window - Team ($25-30/mo): Same limits as Pro with team features - Updated ClaudeLogManager to calculate usage based on selected tier - Added UI in provider settings to select subscription tier - Made calculations dynamic: Free uses daily limits, Pro/Team use 5-hour windows This improves accuracy of token usage tracking by accounting for the different limit structures between Claude's free and paid tiers. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Extract Claude app icon from /Applications/Claude.app - Create claude.imageset with proper Contents.json - Update ServiceProvider to use custom Claude icon instead of generic SF Symbol - Improves visual recognition and consistency with Claude branding 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add correct Claude subscription tiers: Free, Pro ($20), Max 5× ($100), Max 20× ($200) - Implement tier-specific message limits for 5-hour windows - Add subscription tier picker in ProviderDetailView with custom radio buttons - Update ClaudeProvider to use selected tier for calculations - Improve dialog size (600×700) and remove confusing logout button This allows users to configure their actual Claude subscription plan for accurate usage tracking. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Pre-select actual home directory (/Users/username) in file picker, not sandboxed path - Add validation to ensure users select their home directory - Show alert if wrong directory is selected - Validate existing bookmarks on startup and invalidate if pointing to wrong directory - Initialize ClaudeLogManager early in AppDelegate to fix race condition - Show hidden files in picker to make .claude folder visible This prevents the app from parsing wrong directories and ensures reliable Claude log access. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Show Login/Grant Access buttons regardless of provider enabled state - Auto-enable provider when user clicks Login or Grant Access - Shorten error messages to prevent UI truncation - Add line limit and truncation mode for error display - Update footer text to mention current support for Cursor AI and Claude This makes the UI more intuitive - users can immediately see how to connect without needing to first enable providers. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix timing issue where notification was sent before settings window opened - Open settings window first, then post tab switch notification after delay - Ensures Configure Providers button opens directly to Providers tab 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Store weak reference to status bar button in StatusBarMenuManager - Ensure button is unhighlighted when window hides via onHide callback - Override orderOut to catch system-initiated window closures - Remove redundant highlight calls in toggle method The menu bar icon now correctly shows highlighted state when popover is visible and normal state when hidden. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
❌ Build or Tests Failed!Version: 1.2 (1) This PR ran quick tests. Full test suite runs on merge to main. |
- Capture login credentials during initial authentication via JavaScript injection - Store credentials securely in macOS Keychain (service: com.vibemeter.cursor.credentials) - Automatically attempt re-authentication when 401/unauthorized errors occur - Detect CAPTCHA presence and notify user to complete manual verification - Add 30-second timeout for auto-login attempts - Integrate with MultiProviderErrorHandler to trigger auto-auth on auth failures This reduces friction when Cursor session cookies expire, requiring manual intervention only for CAPTCHA challenges. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
…ents - Fix validation logic that was comparing against sandboxed container directory - Accept any directory containing .claude/projects, not just home directory - Add detailed logging to debug bookmark validation issues - Update error messages to be more helpful when wrong directory is selected - Make validation more lenient to handle edge cases where Claude hasn't created logs yet
- Fixed warning about 'remainingText' never being mutated - Changed from var to let since the variable is never modified after initialization
- Enable Asset Catalog Symbol Extensions generation - Enable String Catalog Symbol Generation for localization - Move tiktoken resource file to correct location - Remove duplicate SWIFT_EMIT_LOC_STRINGS setting - These changes prevent Xcode from showing update recommended settings warnings
- Claude uses folder access permissions, not auth tokens - Fix ProvidersSettingsView to handle Claude login differently - Clear any error messages when Claude has valid folder access - Ensure Claude is enabled when folder access is granted - This fixes the confusing error message shown in the UI
- Ensure ProvidersSettingsView properly handles Claude's folder-based auth - Clear errors when Claude has valid access on initialization
- Create LoginConsentView to explain credential storage before login - Show consent dialog first when logging into Cursor - Preload WebView in background while showing consent - Update CAPTCHA notification to be more user-friendly - Explain automatic re-authentication and CAPTCHA requirements - Inform users that credentials are stored securely in macOS Keychain - Add icons and clear information about why we need credentials
- Fixed reference to multiProviderOrchestrator (was using wrong property name 'orchestrator') - Build now succeeds without errors
- Add pre-filtering to skip non-usage log entries (summaries, user messages, etc.) - Check for required JSON structure before attempting to parse - Look for input_tokens and output_tokens fields specifically - Skip entries with sessionId, parentUuid, leafUuid which indicate non-usage data - Reduce log verbosity for expected non-usage entries - Only log parsed entries count when files contain actual usage data This prevents parsing errors from non-usage JSONL entries and improves performance
- Create comprehensive ClaudeUsageReportView showing daily token usage - Add "View Token Report" button in Claude provider row in popover - Add info button in ClaudeQuotaView to open usage report via sheet - Implement usage report button in provider detail settings view - Display daily breakdown with input/output tokens, total usage and cost - Calculate costs at $3/million input tokens, $15/million output tokens - Show summary footer with totals in table view - Use sheet presentation for seamless navigation from popover - Support refresh functionality and error handling in report view 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Move log parsing to background tasks using TaskGroup for concurrent file processing - Add 5-minute in-memory cache to avoid re-parsing logs on every view open - Show detailed loading progress with "Scanning log files..." and "Processing data..." messages - Display enhanced loading UI with larger progress indicator and status text - Update isProcessing state properly across thread boundaries - Add invalidateCache() method for manual refresh functionality - Significantly improve performance when opening token usage report multiple times - Prevent main thread blocking during heavy log file processing 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Remove do-catch wrapper in refreshData Task to prevent compilation errors - Update test files to use proper test assertions and data setup - Fix gauge calculation tests to use correct data structures - Update Claude pricing tier tests with proper account type handling - Improve five-hour window tests with realistic token calculations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Performance optimizations implemented: 1. **Regex-based filtering**: Pre-compiled regex pattern for efficient line matching instead of multiple string contains() calls 2. **File-level caching**: Cache parsed entries per file with modification date checking to avoid re-parsing unchanged files 3. **Streaming file reading**: Use FileHandle with 64KB chunks instead of loading entire files into memory 4. **Skip old files**: Automatically skip files older than 30 days to reduce processing overhead 5. **Sort by modification date**: Process newest files first for better cache hit rates 6. **Optimized parsing**: Quick regex validation before full JSON decode 7. **Background priority**: Use .utility QoS for better system resource management These optimizations significantly reduce parsing time by: - Avoiding re-parsing of unchanged files - Using more efficient string matching - Reducing memory usage with streaming reads - Processing fewer files by date filtering - Better CPU scheduling with appropriate QoS 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Major performance improvements: 1. **UserDefaults Cache**: Store parsed log data and file hashes persistently across app launches 2. **SHA-256 File Hashing**: Use cryptographic hashes instead of modification dates to detect file changes more reliably 3. **Background Actor**: Create dedicated ClaudeLogProcessor actor to handle all parsing off the main thread 4. **True Background Processing**: All file I/O and parsing now happens on background threads with proper priority Benefits: - Cache persists between app launches, making subsequent opens instant - SHA-256 hashing is more reliable than file modification dates - Background actor ensures UI remains responsive during parsing - Proper separation of concerns with dedicated log processing actor - Automatic cache invalidation when files change The ClaudeLogProcessor actor handles all heavy lifting: - Concurrent file processing with TaskGroup - SHA-256 hash calculation for change detection - Efficient chunk-based file reading - Regex-based line filtering 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
When menu bar management apps like Ice hide the VibeMeter icon, the popover would incorrectly appear at the top-left of the screen. This fix adds a fallback mechanism that positions the popover at the top-right corner when the menu bar icon cannot be located or has zero dimensions. - Detect when status button frame is invalid (zero width/height) - Add showAtTopRightFallback() method for predictable positioning - Position popover at top-right corner below menu bar as fallback - Document why fallback is needed for menu bar management apps 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixed several string indexing issues in FastScanner that were causing crashes when parsing Claude log files. The scanner was attempting to access string indices beyond bounds when scanning for tokens. - Rewrite scan() method to use safe string indexing - Fix scanUpTo() to properly check bounds before creating indices - Update scanInteger() and scanWhitespaces() to use safe indexing - Add bounds checking to string subscript extensions - Remove usage of hasPrefix with partial string slices 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Significantly improved scanner performance by caching the string as a character array to avoid expensive String index operations on every character access. - Convert string to character array on initialization - Replace all String index operations with direct array access - Optimize scanUpTo() with first-character quick rejection - Eliminate repeated String index creation in tight loops This reduces the complexity from O(n²) to O(n) for scanning operations, providing much faster Claude log parsing performance. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Applied consistent formatting across multiple files including: - Updated import statements ordering - Fixed spacing and indentation - Replaced manual formatters with VibeMeter extensions - Updated Logger initialization to use VibeMeter extensions - Fixed trailing commas and conditional formatting 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Introduced helper extensions to reduce code duplication: - DateFormatter+VibeMeter: Standard date formatters used across the app - Logger+VibeMeter: Centralized logger with consistent subsystem - NumberFormatter+VibeMeter: Currency and percentage formatters - URLRequest+VibeMeter: Common request configuration helpers - CollectionExtensions: Safe array access and functional helpers These extensions improve code consistency and reduce boilerplate. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Refactored all service classes to use the new extensions: - Replace manual Logger initialization with Logger.vibeMeter - Use standard date formatters from DateFormatter+VibeMeter - Apply NumberFormatter.vibeMeterCurrency for consistent formatting - Update URLRequest configurations to use extension methods This reduces code duplication and ensures consistent behavior across all service implementations. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Applied consistent patterns across all provider implementations: - Use Logger.vibeMeter for logging - Apply URLRequest+VibeMeter for API request configuration - Use DateFormatter extensions for date parsing - Consistent error handling patterns This ensures all providers follow the same conventions and reduces maintenance overhead. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Applied consistent formatting across all UI components: - Use NumberFormatter.vibeMeterCurrency for price display - Apply DateFormatter extensions for consistent date formatting - Use Logger.vibeMeter for logging in UI components - Consistent spacing and formatting This ensures a uniform appearance and behavior across the UI. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Updated all test files to follow the new patterns: - Use extension methods for common operations - Apply consistent formatting and spacing - Add new test utilities for Codable helpers - Update mock implementations to match production code - Add comprehensive test coverage documentation This ensures tests remain in sync with production code patterns. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix trailing newlines in 191 Swift files - Applied consistent formatting across the codebase - Fixed MockService protocol conformance issues - Fixed NotificationManager protocol method names - Added Testing import to ImprovedNotificationManagerMock
❌ Build or Tests Failed!Version: 1.2 (1) This PR ran quick tests. Full test suite runs on merge to main. |
❌ Build or Tests Failed!Version: 1.2 (1) This PR ran quick tests. Full test suite runs on merge to main. |
Document the token parsing bug fix and cache invalidation implementation: - Explain the root cause (incorrect JSON structure expectations) - Detail the changes made to support cache tokens and costUSD - Document the automatic cache invalidation system 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
❌ Build or Tests Failed!Version: 1.2 (1) This PR ran quick tests. Full test suite runs on merge to main. |
Summary
This PR implements Claude AI usage tracking in VibeMeter by parsing local log files, providing a seamless multi-provider experience alongside the existing Cursor integration.
Key Features
~/.claude/projects/with secure sandbox accessImplementation Details
Core Components
Tiktoken/- Swift implementation of OpenAI's tokenizer libraryClaudeLogManager- Manages file access with security-scoped bookmarks (refactored from singleton for testability)ClaudeProvider- Provider implementation using local file access instead of APIsClaudeUsageData.swift- Data models for log entries and usage trackingClaudeDetailView- UI for displaying daily usage breakdownClaudeQuotaView- Shows 5-hour window usage in the popoverArchitecture Changes
UI Enhancements
Testing
Documentation
Screenshots
Note: The implementation follows the design specifications from claude.md
🤖 Generated with Claude Code