Version: 2.5.0 Last Updated: 2025-11-24 Purpose: Technical architecture reference for Claude AI assistant sessions
- Overview
- Project Structure
- Core Architecture
- Module Responsibilities
- Data Flow Diagrams
- Configuration System
- External Dependencies
- Performance Characteristics
- Build System (v2.5.0+)
- Common Development Patterns
- Troubleshooting Guide
A VS Code extension providing dual monitoring capabilities for Claude usage:
- Claude.ai Web Usage - Tracks 5-hour usage limit via web scraping (with API fallback)
- Claude Code Token Consumption - Real-time monitoring of development session token usage through JSONL file tracking
- Dual Data Sources - Independent monitoring of web usage and local tokens
- Smart Browser Management - Connection pooling, lazy launch, intelligent headless mode
- Progressive Enhancement - Status bar → Tooltip → Tree view (increasing detail)
- Resilient Architecture - Each data source fails independently without breaking the other
- Performance Optimized - API-first with HTML fallback, minimal resource usage
- Bundled Distribution - esbuild bundling for 85% smaller package size (v2.5.0+)
- Total Source Lines: ~2,023 lines of JavaScript
- Main Entry: extension.js (309 lines)
- Modules: 8 specialized modules in src/
- Auto-refresh: Configurable 1-60 minutes (default 5 min)
- Token Tracking: Real-time via file watcher + 30-second polling
/root/vscode-extensions/claude-usage/
├── extension.js # Main entry point (309 lines) - SOURCE
├── esbuild.js # Build configuration for bundling (46 lines)
├── cleanup-browser.js # Browser lock file cleanup utility (40 lines)
├── package.json # Extension manifest with build scripts
├── package-lock.json # Dependency lock file
├── LICENSE # MIT License
├── README.md # User-facing documentation
├── CHANGELOG.md # Version history
├── CLAUDE.md # Development guidelines
├── QUICKSTART.md # Quick setup guide
├── TESTING.md # Testing documentation
│
├── src/ # Source modules (bundled into dist/)
│ ├── activityMonitor.js # Usage level calculator (93 lines)
│ ├── claudeDataLoader.js # JSONL parser for token data (376 lines)
│ ├── dataProvider.js # Tree view data provider (280 lines)
│ ├── scraper.js # Puppeteer web scraper (500 lines)
│ ├── sessionTracker.js # Session management (123 lines)
│ ├── statusBar.js # Status bar UI (162 lines)
│ ├── usageHistory.js # Historical data & sparklines (142 lines)
│ └── utils.js # Shared utilities (38 lines)
│
├── dist/ # Bundled output (published to marketplace)
│ ├── extension.js # Bundled entry point (~2.3 MB minified)
│ └── extension.js.map # Source map for debugging (~1.9 MB)
│
├── icons/ # Extension icons
│ ├── claude-usage-icon.png # Marketplace icon
│ └── claude-usage-tree-icon.svg # Activity bar icon
│
├── screenshots/ # Documentation screenshots
│ ├── status-bar.png
│ ├── tooltip.png
│ ├── tree-view.png
│ └── settings.png
│
├── assets/ # Demo assets
│ └── demo.gif
│
├── .vscode/ # VS Code configuration
│ └── launch.json # Debug configuration
│
├── .github/ # GitHub Actions workflows
│ └── workflows/
│
├── archive/ # Previous .vsix versions
│ └── [old versions]
│
├── node_modules/ # Dependencies (NOT published, bundled instead)
│
└── .vscodeignore # Excludes src/, extension.js, includes dist/
VS Code Startup
↓
onStartupFinished activation event
↓
activate(context) in extension.js
↓
┌─────────────────────────────────────────────┐
│ 1. Create Core Components │
│ - Status bar item │
│ - Data provider (tree view) │
│ - Activity monitor │
│ - Session tracker │
│ - Claude data loader │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 2. Setup Token Monitoring │
│ - Find Claude data directory │
│ - Setup file watcher (*.jsonl) │
│ - Start 30-second polling backup │
│ - Load initial token usage │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 3. Register VS Code Integration │
│ - Commands (fetchNow, openSettings, etc.)│
│ - Tree view container (activity bar) │
│ - Configuration listeners │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 4. Optional: Fetch on Startup │
│ if (claudeUsage.fetchOnStartup === true) │
│ → Triggers web scraping immediately │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 5. Start Auto-Refresh Timer │
│ Interval: claudeUsage.autoRefreshMinutes │
│ Default: 5 minutes │
│ → Periodic web scraping + token refresh │
└─────────────────────────────────────────────┘
deactivate() called
↓
Clear auto-refresh timer
↓
Close Puppeteer browser
↓
Dispose file watchers
↓
Clean up resources
Lines: 309 Role: Orchestrates all components and manages extension lifecycle
Key Functions:
| Function | Purpose | Key Operations |
|---|---|---|
activate(context) |
Extension entry point | Creates components, registers commands, starts monitoring |
setupTokenMonitoring(context) |
Initialize JSONL tracking | Sets up file watcher + polling, loads initial data |
updateTokensFromJsonl(channel, silent) |
Update token usage | Parses JSONL → updates session tracker → refreshes UI |
updateStatusBarWithAllData() |
Refresh all UI | Updates status bar + tree view with latest data |
deactivate() |
Cleanup on shutdown | Clears timers, closes browser, disposes watchers |
Global State Variables:
let statusBarItem; // VS Code status bar item
let dataProvider; // Tree view data provider
let autoRefreshTimer; // Interval for periodic refresh
let activityMonitor; // Usage level calculator
let sessionTracker; // Session management
let claudeDataLoader; // JSONL parser
let jsonlWatcher; // File system watcherCommand Registration:
claude-usage.fetchNow→dataProvider.fetchUsage()claude-usage.openSettings→ Opens https://claude.ai/settingsclaude-usage.startNewSession→ Prompts for description, creates new sessionclaude-usage.updateTokens→ Test command for manual token updates
Lines: 500 Role: Fetches Claude.ai usage data via Puppeteer with dual-mode (API + HTML)
Architecture: Smart browser management with connection pooling
Key Functions:
| Function | Purpose | Details |
|---|---|---|
initialize(forceHeaded) |
Launch/connect browser | Tries port 9222 connection first, falls back to launch |
hasExistingSession() |
Check for saved cookies | Looks in ~/.claude-browser-session/ |
ensureLoggedIn() |
Wait for authentication | Max 5 minutes, shows browser if needed |
setupRequestInterception() |
Capture API endpoints | Intercepts /api/organizations/*/usage requests |
fetchUsageData() |
CORE - Get usage data | API mode (preferred) or HTML mode (fallback) |
processApiResponse(apiResponse) |
Parse API JSON | Extracts five_hour.utilization (deprecated seven_day returns 0) |
calculateResetTime(isoTimestamp) |
Convert ISO to "2h 30m" | Human-readable time until reset |
close() |
Smart cleanup | Disconnect if connected, close if launched |
Dual-Mode Data Fetching:
Priority 1: API Mode (2-3x faster, more reliable)
// Captured during request interception:
GET /api/organizations/{org_id}/usage
Response:
{
"five_hour": {
"utilization": 0.45, // 45%
"reset_at": "2025-11-19T23:30:00.000Z"
}
// Note: "seven_day" field was deprecated by Claude.ai
}Priority 2: HTML Mode (fallback if API unavailable)
// Regex patterns:
/(\d+)%\s*used/ → Percentage
/Resets?\s+in\s+([^\n]+)/ → Reset timeSession Persistence:
- Cookie storage:
~/.claude-browser-session/ - Persists for weeks
- No re-login needed on subsequent runs
Chrome Detection: Searches for executables in order:
- Google Chrome (standard install)
- Scoop package manager location
- Microsoft Edge (Chromium-based)
Lines: 376 Role: Parses Claude Code's JSONL files for token usage data
Data Source: ~/.config/claude/projects/**/*.jsonl (or ~/.claude/projects/**/*.jsonl)
Key Functions:
| Function | Purpose | Algorithm |
|---|---|---|
findClaudeDataDirectory() |
Locate Claude data folder | Checks multiple locations, validates existence |
findJsonlFiles(dirPath) |
Find all .jsonl files | Recursive directory traversal |
parseJsonlFile(filePath) |
Parse JSONL to objects | Line-by-line JSON parsing, validates records |
getCurrentSessionUsage() |
CORE - Get current session tokens | Finds active conversation, extracts cache size |
loadUsageRecords(sinceTimestamp) |
Aggregate historical usage | Filters by timestamp, deduplicates |
isValidUsageRecord(record) |
Validate record structure | Filters out synthetic messages |
Current Session Detection Algorithm:
1. Find all .jsonl files
↓
2. Filter files modified in last 5 minutes (active conversation)
↓
3. Sort by modification time (newest first)
↓
4. Read most recent file from END to START
↓
5. Find last "assistant" message with usage data
↓
6. Extract cache_read_input_tokens (approximates context size)
↓
7. Return token breakdown:
- inputTokens: Tokens in prompts
- outputTokens: Tokens in responses
- cacheReadTokens: Tokens read from cache (SESSION TOTAL)
- cacheCreationTokens: Tokens used to create cache
Why cache_read_tokens?
- Represents the total context size Claude is reading
- Approximates current session token consumption
- More accurate than summing individual messages (avoids double-counting)
JSONL Record Format:
{
"type": "usage",
"inputTokens": 1234,
"outputTokens": 567,
"cacheCreationInputTokens": 100,
"cacheReadInputTokens": 45000,
"id": "msg_abc123",
"requestId": "req_xyz789"
}Deduplication: Uses message.id + requestId hash to avoid counting duplicates across file reads
Lines: 280 Role: Provides data for VS Code tree view panel in activity bar
Implements: vscode.TreeDataProvider<TreeItem>
Key Functions:
| Function | Purpose | Returns |
|---|---|---|
getChildren(element) |
Build tree view items | Array of TreeItem objects |
getTreeItem(element) |
Convert to VS Code TreeItem | TreeItem with icon, label, tooltip |
refresh() |
Trigger UI refresh | Fires onDidChangeTreeData event |
fetchUsage() |
Orchestrate web scraping | Calls scraper, shows progress, updates tree |
updateSessionData(sessionData) |
Update token display | Refreshes tree with new token counts |
Tree View Structure:
Claude Usage
├── 📊 Usage (5-hour): 45%
│ ├── 📈 Sparkline: ▁▁▂▂▃▃▄▅▅▆▆▇▇█▇▆▆▅▅▄▃▃▂▂ (4 hours)
│ └── ⏰ Resets in: 2h 30m (14:30)
├── 🎯 Session Tokens: 45,234 / 200,000 (~23%)
│ ├── ↗️ Input: 20,123 tokens
│ ├── ↙️ Output: 15,111 tokens
│ ├── 💾 Cache Read: 45,234 tokens
│ └── 🔧 Cache Creation: 10,000 tokens
└── 🕐 Last updated: 2:45 PM
Icon System:
- Critical (≥90%):
$(error)Red error icon - Warning (≥80%):
$(warning)Orange warning icon - Normal (<80%):
$(pass)Green check icon - Time:
$(clock)Clock icon - Trending:
$(graph)Graph icon
Progress Notification:
- Shows VS Code progress bar during web scraping
- Displays "Fetching Claude usage data..." message
- Cancellable by user
Lines: 123 Role: Tracks Claude Code development sessions and token usage
Data Storage: %TEMP%\claude-session-data.json (Windows) or /tmp/claude-session-data.json (Mac/Linux)
Key Functions:
| Function | Purpose | Operation |
|---|---|---|
startSession(description) |
Create new session | Generates unique ID, initializes with 0 tokens |
updateTokens(tokensUsed, tokenLimit) |
Update current session | Sets current/limit/remaining, saves to disk |
getCurrentSession() |
Get active session | Returns current session data or null |
loadData() / saveData() |
Persistence | Reads/writes JSON file |
Session ID Format: session-YYYY-MM-DD-###
- Example:
session-2025-11-19-001 - Auto-increments daily counter
Data Structure:
{
"sessions": [
{
"sessionId": "session-2025-11-19-001",
"startTime": "2025-11-19T10:30:00.000Z",
"description": "Implementing user authentication",
"tokenUsage": {
"current": 45000,
"limit": 200000,
"remaining": 155000,
"lastUpdate": "2025-11-19T11:15:00.000Z"
}
}
],
"totals": {
"totalSessions": 1,
"totalTokensUsed": 45000,
"lastSessionDate": "2025-11-19T10:30:00.000Z"
}
}Lines: 162 Role: Manages status bar item display and tooltips
Key Functions:
| Function | Purpose | Updates |
|---|---|---|
createStatusBarItem(context) |
Create status bar item | Right-aligned, high priority |
updateStatusBar(item, usageData, activityStats, sessionData) |
Update display | Text, color, tooltip, command |
Status Bar Display:
Text: $(icon) Claude: 45% | Tokens: ~26%
Color: $(warning) Orange (warning level)
Click: Opens tree view panel
Tooltip (Markdown Format):
**Claude.ai Web Usage:**
- 5-hour: 45% (resets in 2h 30m)
**Session Token Usage:**
- Current: 45,234 / 200,000 (~23%)
**Usage Level:** Moderate (50-79%)
Much work, many thought
*Last updated: 2:45 PM*
*Extension version: 2.3.9*
*Click to view details*Icon Selection Based on Activity Level:
idle: new vscode.ThemeIcon('pass', new vscode.ThemeColor('charts.green'))
light: new vscode.ThemeIcon('info', new vscode.ThemeColor('charts.blue'))
moderate: new vscode.ThemeIcon('warning', new vscode.ThemeColor('charts.orange'))
heavy: new vscode.ThemeIcon('error', new vscode.ThemeColor('charts.red'))Lines: 93 Role: Calculates overall usage level based on both Claude.ai and token usage
Key Functions:
| Function | Purpose | Returns |
|---|---|---|
getActivityLevel(usageData, sessionData) |
Determine usage level | 'idle' | 'light' | 'moderate' | 'heavy' |
getStats(usageData, sessionData) |
Calculate detailed stats | Object with percentages and level |
getActivityDescription(level) |
Human-readable description | Flavor text for each level |
Level Thresholds:
Heavy: 80-100% "Running low!"
Moderate: 50-79% "Much work, many thought"
Light: 25-49% "Quarter+ used"
Idle: 0-24% "Plenty of Claude time!"Algorithm:
function getActivityLevel(usageData, sessionData) {
const claudePercent = Math.max(
usageData.fiveHourUsage || 0,
usageData.sevenDayUsage || 0
);
const tokenPercent = sessionData.tokenUsage
? (sessionData.tokenUsage.current / sessionData.tokenUsage.limit) * 100
: 0;
const maxPercent = Math.max(claudePercent, tokenPercent);
if (maxPercent >= 80) return 'heavy';
if (maxPercent >= 50) return 'moderate';
if (maxPercent >= 25) return 'light';
return 'idle';
}Why MAX of both percentages?
- Conservative approach: Shows highest urgency
- User sees worst-case scenario
- Prevents surprise rate limiting
Lines: 142 Role: Stores historical usage data and generates ASCII sparklines
Data Storage: %TEMP%\claude-usage-history.json
Key Functions:
| Function | Purpose | Algorithm |
|---|---|---|
addDataPoint(fiveHourUsage) |
Save new data point | Appends with timestamp, auto-cleanup old data |
getRecentDataPoints(count) |
Retrieve last N points | Returns array of {timestamp, value} |
generateSparkline(values) |
Convert numbers to blocks | Maps 0-100% to ▁▂▃▄▅▆▇█ characters |
getFiveHourSparkline(count) |
Get sparkline string | Calls generateSparkline with last N values |
Sparkline Algorithm:
function generateSparkline(values) {
const blocks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
// Normalize to 0-7 range
const min = Math.min(...values);
const max = Math.max(...values);
const range = max - min || 1;
return values.map(v => {
const normalized = ((v - min) / range) * 7;
const index = Math.min(7, Math.floor(normalized));
return blocks[index];
}).join('');
}Example Output: ▁▁▂▂▃▃▄▅▅▆▆▇▇█▇▆▆▅▅▄▃▃▂▂ (24 chars, 4-hour trend)
Data Retention:
- Max data points: 48 (4 hours at 5-minute intervals)
- Sparkline display: 24 characters (each representing 10 minutes via 2-point average)
- Auto-cleanup removes points older than 48 entries
- Persisted across VS Code restarts
Lines: 38 Role: Shared utility functions
Key Functions:
| Function | Purpose | Example |
|---|---|---|
calculateResetClockTime(resetTime) |
Convert relative time to clock time | "2h 30m" → "14:30" |
Algorithm:
// Input: "2h 30m" or "6d 12h" or "45m"
// Output: "14:30" or "Nov 26, 10:00"
1. Parse with regex: /(\d+)d/, /(\d+)h/, /(\d+)m/
2. Convert to milliseconds
3. Add to current time
4. Format as HH:MM or "Mon DD, HH:MM"User Action (Click status bar / Auto-refresh timer)
↓
dataProvider.fetchUsage()
↓
┌──────────────────────────────────────────┐
│ 1. Initialize Browser │
│ scraper.initialize() │
│ ├─ Try connect to port 9222 │
│ ├─ Fall back to launch new browser │
│ └─ Check for saved session cookies │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 2. Ensure Authentication │
│ scraper.ensureLoggedIn() │
│ ├─ If has session: Continue │
│ └─ If no session: Show browser, wait │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 3. Fetch Usage Data (Dual Mode) │
│ scraper.fetchUsageData() │
│ │
│ Priority 1: API Mode │
│ ├─ Navigate to settings page │
│ ├─ Capture /api/.../usage endpoint │
│ ├─ Make direct fetch to API │
│ └─ processApiResponse() │
│ ↓ │
│ Priority 2: HTML Mode (fallback) │
│ ├─ Parse page body text │
│ ├─ Regex extract: /(\d+)%\s*used/ │
│ └─ Regex extract: /Resets?\s+in\s+/ │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 4. Store & Process │
│ usageHistory.addDataPoint() │
│ dataProvider.usageData = result │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 5. Update UI │
│ updateStatusBarWithAllData() │
│ ├─ statusBar.updateStatusBar() │
│ └─ dataProvider.refresh() │
└──────────────────────────────────────────┘
Extension Activation
↓
setupTokenMonitoring()
↓
┌──────────────────────────────────────────┐
│ 1. Find Claude Data Directory │
│ claudeDataLoader.findClaudeDataDirectory() │
│ ├─ Check ~/.config/claude/projects │
│ ├─ Check ~/.claude/projects │
│ └─ Check $CLAUDE_CONFIG_DIR │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 2. Setup Dual Monitoring │
│ │
│ A) File Watcher (Real-time) │
│ vscode.workspace.createFileSystemWatcher() │
│ ├─ Pattern: **/*.jsonl │
│ ├─ onDidChange: updateTokens() │
│ └─ onDidCreate: updateTokens() │
│ │
│ B) Polling Backup (Every 30 seconds) │
│ setInterval(updateTokens, 30000) │
│ └─ Safety net for missed events │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 3. Update Token Usage │
│ updateTokensFromJsonl() │
│ ↓ │
│ claudeDataLoader.getCurrentSessionUsage() │
│ ├─ Find files modified in last 5 min │
│ ├─ Read most recent JSONL │
│ ├─ Parse from end to start │
│ ├─ Find last "assistant" message │
│ └─ Extract cache_read_input_tokens │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ 4. Store & Distribute │
│ sessionTracker.updateTokens() │
│ ↓ │
│ updateStatusBarWithAllData() │
│ ├─ statusBar.updateStatusBar() │
│ └─ dataProvider.updateSessionData() │
└──────────────────────────────────────────┘
activityMonitor.getStats(usageData, sessionData)
↓
┌──────────────────────────────────────────┐
│ Extract Web Usage │
│ claudePercent = max( │
│ usageData.fiveHourUsage, │
│ usageData.sevenDayUsage │
│ ) │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ Extract Token Usage │
│ tokenPercent = ( │
│ sessionData.current / │
│ sessionData.limit │
│ ) * 100 │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ Determine Maximum (Conservative) │
│ maxPercent = max( │
│ claudePercent, │
│ tokenPercent │
│ ) │
└──────────────────────────────────────────┘
↓
┌──────────────────────────────────────────┐
│ Map to Activity Level │
│ if maxPercent >= 80 → 'heavy' │
│ if maxPercent >= 50 → 'moderate' │
│ if maxPercent >= 25 → 'light' │
│ else → 'idle' │
└──────────────────────────────────────────┘
↓
Return: {
level: 'moderate',
claudePercent: 45,
tokenPercent: 23,
maxPercent: 45,
description: 'Much work, many thought'
}
Extension Metadata:
{
"name": "claude-session-usage",
"displayName": "Claude Session Usage",
"version": "2.3.9",
"publisher": "Gronsten",
"engines": { "vscode": "^1.80.0" }
}Activation:
- Event:
onStartupFinished - Main:
./extension.js - Category: "Other"
Commands:
| Command ID | Title | Handler |
|---|---|---|
claude-usage.fetchNow |
Fetch Claude Usage Now | dataProvider.fetchUsage() |
claude-usage.openSettings |
Open Claude Settings Page | Opens https://claude.ai/settings |
claude-usage.startNewSession |
Start New Claude Code Session | Prompts for description, creates session |
claude-usage.updateTokens |
Update Token Usage (Manual Test) | Test command (hidden from palette) |
Views Configuration:
{
"viewsContainers": {
"activitybar": [{
"id": "claude-usage",
"title": "Claude Usage",
"icon": "icons/claude-usage-tree-icon.svg"
}]
},
"views": {
"claude-usage": [{
"id": "claude-usage-view",
"name": "Session Usage"
}]
}
}Settings:
| Setting | Type | Default | Range | Description |
|---|---|---|---|---|
claudeUsage.fetchOnStartup |
boolean | true |
- | Auto-fetch on VS Code startup |
claudeUsage.headless |
boolean | true |
- | Run browser in headless mode (shows if login needed) |
claudeUsage.autoRefreshMinutes |
number | 5 |
1-60 | Auto-refresh interval in minutes |
Dependencies:
{
"dependencies": {
"puppeteer": "^24.15.0"
},
"devDependencies": {
"@types/vscode": "^1.80.0"
}
}puppeteer (^24.15.0)
- Purpose: Chromium automation for web scraping
- Download Size: ~150-200MB (includes Chromium)
- Features Used:
- Browser launch and connection
- Page navigation and interaction
- Request interception (API capture)
- Cookie/session persistence
- Headless and headed modes
@types/vscode (^1.80.0)
- Purpose: TypeScript definitions for VS Code extension API
- Use: Type checking and IntelliSense (even for JavaScript projects)
| Module | Usage |
|---|---|
fs / fs.promises |
File system operations (read JSONL, save session data) |
path |
Path manipulation (join, resolve, dirname) |
os |
OS information (homedir, tmpdir) |
vscode |
VS Code extension API (commands, views, status bar, etc.) |
| Metric | Value | Notes |
|---|---|---|
| Web Scraping | 2-10 seconds | Depends on network speed and API vs HTML mode |
| JSONL Parsing | <100ms | Native Node.js, no external deps |
| File Watching | Instant | OS-level file system notifications |
| Memory Usage | ~150-200MB | Chromium browser when running |
| Disk Usage | ~150-200MB | Puppeteer Chromium download |
| Auto-refresh Impact | Minimal | Browser closes between fetches |
- API-First Fetching: 2-3x faster than HTML scraping
- Browser Connection Pooling: Reuses existing Chrome instance
- Smart Headless Mode: Shows browser only when authentication needed
- Lazy Browser Launch: Only launches when fetching data
- Incremental JSONL Parsing: Reads from end-to-start (stops at first match)
- 30-Second Polling: Balance between real-time and CPU usage
Starting with v2.5.0, the extension uses esbuild for bundling all source files and dependencies into a single optimized file.
Benefits:
- 85% smaller package: 1.16 MB (down from 8.06 MB)
- Faster loading: Single bundled file reduces I/O operations
- Better performance: Production builds are minified
- Cleaner distribution: No node_modules in .vsix package
esbuild.js handles the bundling process:
{
entryPoints: ['extension.js'], // Main entry
bundle: true, // Bundle all imports
format: 'cjs', // CommonJS format
minify: production, // Minify for production
sourcemap: !production, // Source maps for dev
platform: 'node', // Node.js environment
outfile: 'dist/extension.js', // Output location
external: ['vscode', 'typescript'] // Don't bundle these
}Added to package.json:
{
"scripts": {
"build": "node esbuild.js", // Dev build
"watch": "node esbuild.js --watch", // Watch mode
"package": "node esbuild.js --production && vsce package", // Build + package
"publish": "node esbuild.js --production && vsce publish" // Build + publish
}
}For development (with sourcemaps):
npm run build # One-time build
npm run watch # Auto-rebuild on changesFor packaging:
npm run package # Builds with --production (minified) + creates .vsixFor publishing:
npm run publish # Builds with --production + publishes to marketplaceIncluded in bundle:
- extension.js and all src/ modules
- All dependencies from node_modules (except externals)
- Result: Single dist/extension.js file
Excluded (marked as external):
vscode- Provided by VS Code runtimetypescript- Optional dependency, not needed at runtime
Not published (.vscodeignore):
- Source files (extension.js, src/)
- Build configuration (esbuild.js)
- node_modules/ directory
- Only dist/ is included in .vsix
| Aspect | Before (v2.4.0) | After (v2.5.0) | Improvement |
|---|---|---|---|
| Package Size | 8.06 MB | 1.16 MB | 85% smaller |
| File Count | 3,054 files | 18 files | 99% fewer files |
| Build Time | N/A | 1-2 seconds | Fast builds |
| Load Time | Slower | Faster | Reduced I/O |
Step 1: Register in package.json
{
"contributes": {
"commands": [{
"command": "claude-usage.myNewCommand",
"title": "My New Command",
"category": "Claude"
}]
}
}Step 2: Implement in extension.js
// In activate(context)
context.subscriptions.push(
vscode.commands.registerCommand('claude-usage.myNewCommand', async () => {
// Implementation here
vscode.window.showInformationMessage('Command executed!');
})
);Modify dataProvider.js:
async getChildren(element) {
const items = await super.getChildren(element);
// Add new item
items.push(new vscode.TreeItem(
'My New Item',
vscode.TreeItemCollapsibleState.None
));
return items;
}Step 1: Add to package.json
{
"configuration": {
"properties": {
"claudeUsage.myNewSetting": {
"type": "boolean",
"default": true,
"description": "My new setting description"
}
}
}
}Step 2: Read in code
const config = vscode.workspace.getConfiguration('claudeUsage');
const myValue = config.get('myNewSetting');Step 3: Listen for changes
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('claudeUsage.myNewSetting')) {
// Handle change
}
});Workflow Checklist:
# 1. Update version in package.json
"version": "2.5.1"
# 2. Update CHANGELOG.md
### v2.5.1 (2025-XX-XX)
- Added: New feature description
# 3. Build and package the extension (includes esbuild bundling)
npm run package
# This runs: node esbuild.js --production && vsce package
# Output: claude-session-usage-2.5.1.vsix (bundled, ~1.2 MB)
# 4. Move old .vsix to archive
mv claude-session-usage-2.5.0.vsix archive/
# 5. Test the new .vsix
# Install in VS Code and verify all functionality works
# 6. Create feature branch and commit
git checkout -b feature/my-feature
git add .
git commit -S -m "Add new feature (v2.5.1)"
git push -u origin feature/my-feature
# 7. Create and merge PR
gh pr create --title "..." --body "..." --base main
# After approval:
gh pr merge <number> --squash --delete-branch
# 8. Create GitHub release
git checkout main && git pull --rebase origin main
gh release create v2.5.1 --title "v2.5.1 - Feature Name" \
--notes "Release notes..."
# 9. Publish to marketplace (includes esbuild bundling)
npm run publish
# This runs: node esbuild.js --production && vsce publishNote: v2.5.0+ uses esbuild bundling. Always use npm run package or npm run publish instead of direct vsce commands to ensure bundling happens first.
VS Code Debug Configuration (.vscode/launch.json):
{
"type": "extensionHost",
"request": "launch",
"name": "Launch Extension",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceFolder}"]
}Debugging Steps:
- Press
F5to launch Extension Development Host - Set breakpoints in source files
- Open "Debug Console" for output
- Use "Output" → "Extension Host" for logs
- Run commands in development host to trigger breakpoints
Issue: Token usage not updating
Symptoms: Status bar shows stale token counts
Diagnosis:
- Check "Claude Usage - Token Monitor" output channel
- Verify Claude Code is saving to
~/.config/claude/projects/ - Check file watcher has permissions
Solution:
// Check output channel for diagnostic logs
// Look for:
"✅ Found Claude data directory: /path/to/.config/claude"
"📊 Current session: X tokens (Y messages)"Issue: Web scraping fails with "Login required"
Symptoms: Browser shows login page, extension can't fetch data
Diagnosis:
- Session cookies expired
- Claude.ai changed authentication flow
Solution:
# 1. Delete saved session
rm -rf ~/.claude-browser-session/
# 2. Manually fetch usage (browser will show)
# Command Palette → "Fetch Claude Usage Now"
# 3. Log in when browser appears
# Extension will save new sessionIssue: Browser won't close / lock file error
Symptoms: Multiple browser instances, "UserDataDir is already in use"
Diagnosis: Previous browser instance didn't close cleanly
Solution:
# Run cleanup utility
node cleanup-browser.js
# Or manually:
rm -rf /tmp/puppeteer-* # Mac/Linux
# Windows: Delete %TEMP%\puppeteer-* foldersIssue: JSONL files not found
Symptoms: Output channel shows "Claude data directory not found"
Diagnosis: Claude Code data in non-standard location
Solution:
# Set custom path in environment
export CLAUDE_CONFIG_DIR="/custom/path/to/claude"
# Or check where Claude Code is saving:
ls ~/.config/claude/projects/*.jsonl
ls ~/.claude/projects/*.jsonlIssue: High memory usage
Symptoms: VS Code slow, high RAM consumption
Diagnosis: Puppeteer browser not closing
Solution:
- Ensure
autoRefreshMinutesis reasonable (≥5 minutes) - Check for multiple browser processes (
ps aux | grep chrome) - Restart VS Code to clean up
Issue: Sparklines not showing
Symptoms: Tree view shows "N/A" for sparklines
Diagnosis: Not enough historical data points
Solution:
- Wait for at least 2 auto-refresh cycles
- Manually fetch usage to populate history
- Check
claude-usage-history.jsonexists
| Purpose | Windows | Mac/Linux |
|---|---|---|
| Session data | %TEMP%\claude-session-data.json |
/tmp/claude-session-data.json |
| Usage history | %TEMP%\claude-usage-history.json |
/tmp/claude-usage-history.json |
| Browser session | %USERPROFILE%\.claude-browser-session\ |
~/.claude-browser-session/ |
| Claude JSONL | %USERPROFILE%\.config\claude\projects\ |
~/.config/claude/projects/ |
| Feature | File | Lines |
|---|---|---|
| Main entry point | extension.js | 1-309 |
| Web scraping | scraper.js | 1-500 |
| JSONL parsing | claudeDataLoader.js | 1-376 |
| Tree view | dataProvider.js | 1-280 |
| Token monitoring setup | extension.js | 21-83 |
| Current session detection | claudeDataLoader.js | 200-250 |
| Dual-mode fetching | scraper.js | 150-300 |
- → Sequential flow
- ├─ Branch/option
- └─ Final branch/end
- ↓ Continues downward
- $(icon) VS Code ThemeIcon
End of Architecture Document
For user-facing documentation, see README.md. For quick setup, see QUICKSTART.md.