#!/usr/bin/env node /** * Runtime Token Tracking & Alert System for BMAD * Provides real-time token monitoring, alerts, and automatic checkpoint triggers */ const EventEmitter = require('events'); const fs = require('fs'); const path = require('path'); class TokenTracker extends EventEmitter { constructor(options = {}) { super(); // Configuration this.budget = options.budget || 5000; this.checkpointThreshold = options.checkpointThreshold || 3000; this.warningThreshold = options.warningThreshold || 0.75; this.criticalThreshold = options.criticalThreshold || 0.9; // State tracking this.currentTokens = 0; this.contextHistory = []; this.loadedFiles = new Map(); this.startTime = Date.now(); this.checkpointCount = 0; // Telemetry this.metrics = { totalLoaded: 0, totalSaved: 0, checkpoints: [], alerts: [], fileAccess: new Map() }; this.setupAlerts(); } /** * Setup alert handlers */ setupAlerts() { this.on('warning', (data) => { console.warn(`āš ļø TOKEN WARNING: ${data.message}`); this.metrics.alerts.push({ level: 'warning', ...data, timestamp: Date.now() }); }); this.on('critical', (data) => { console.error(`🚨 TOKEN CRITICAL: ${data.message}`); this.metrics.alerts.push({ level: 'critical', ...data, timestamp: Date.now() }); }); this.on('checkpoint-needed', (data) => { console.log(`šŸ“ CHECKPOINT NEEDED: ${data.message}`); this.metrics.alerts.push({ level: 'checkpoint', ...data, timestamp: Date.now() }); }); } /** * Track file loading */ loadFile(filepath, content) { const tokens = this.estimateTokens(content); const filename = path.basename(filepath); // Check if would exceed budget if (this.currentTokens + tokens > this.budget) { this.emit('critical', { message: `Loading ${filename} (${tokens} tokens) would exceed budget`, current: this.currentTokens, attempted: tokens, budget: this.budget }); return false; } // Track the load this.loadedFiles.set(filepath, { tokens, loadedAt: Date.now(), accessCount: 1 }); this.currentTokens += tokens; this.metrics.totalLoaded += tokens; // Update file access metrics const access = this.metrics.fileAccess.get(filepath) || { count: 0, tokens: 0 }; access.count++; access.tokens += tokens; this.metrics.fileAccess.set(filepath, access); // Add to history this.contextHistory.push({ action: 'load', file: filename, tokens, total: this.currentTokens, timestamp: Date.now() }); this.checkStatus(); return true; } /** * Unload file from context */ unloadFile(filepath) { const file = this.loadedFiles.get(filepath); if (file) { this.currentTokens -= file.tokens; this.loadedFiles.delete(filepath); this.contextHistory.push({ action: 'unload', file: path.basename(filepath), tokens: -file.tokens, total: this.currentTokens, timestamp: Date.now() }); } } /** * Create checkpoint */ createCheckpoint(summary, compressedTokens) { const checkpoint = { id: `checkpoint-${++this.checkpointCount}`, timestamp: Date.now(), originalTokens: this.currentTokens, compressedTokens, compressionRatio: ((this.currentTokens - compressedTokens) / this.currentTokens * 100).toFixed(1), files: Array.from(this.loadedFiles.keys()), summary }; this.metrics.checkpoints.push(checkpoint); this.metrics.totalSaved += (this.currentTokens - compressedTokens); // Reset context this.currentTokens = compressedTokens; this.loadedFiles.clear(); this.contextHistory.push({ action: 'checkpoint', checkpoint: checkpoint.id, saved: checkpoint.originalTokens - compressedTokens, timestamp: Date.now() }); return checkpoint; } /** * Check current status and emit alerts */ checkStatus() { const usage = this.currentTokens / this.budget; const status = this.getStatus(); // Checkpoint threshold if (this.currentTokens > this.checkpointThreshold) { this.emit('checkpoint-needed', { message: `Context exceeds checkpoint threshold (${this.currentTokens}/${this.checkpointThreshold})`, current: this.currentTokens, threshold: this.checkpointThreshold }); } // Budget alerts if (usage > this.criticalThreshold && status.previous !== 'critical') { this.emit('critical', { message: `Token usage critical: ${(usage * 100).toFixed(1)}% of budget`, current: this.currentTokens, budget: this.budget }); } else if (usage > this.warningThreshold && status.previous === 'green') { this.emit('warning', { message: `Token usage warning: ${(usage * 100).toFixed(1)}% of budget`, current: this.currentTokens, budget: this.budget }); } } /** * Get current status */ getStatus() { const usage = this.currentTokens / this.budget; let status, emoji, color; if (usage < 0.5) { status = 'green'; emoji = '🟢'; color = '\x1b[32m'; } else if (usage < this.warningThreshold) { status = 'yellow'; emoji = '🟔'; color = '\x1b[33m'; } else if (usage < this.criticalThreshold) { status = 'orange'; emoji = '🟠'; color = '\x1b[38;5;208m'; } else { status = 'critical'; emoji = 'šŸ”“'; color = '\x1b[31m'; } return { status, emoji, color, usage: (usage * 100).toFixed(1), current: this.currentTokens, budget: this.budget, remaining: this.budget - this.currentTokens }; } /** * Estimate tokens (simplified) */ estimateTokens(content) { const words = content.match(/\b\w+\b/g) || []; return Math.ceil(words.length * 1.3); } /** * Get recommendations based on current state */ getRecommendations() { const recommendations = []; const status = this.getStatus(); if (status.status === 'critical') { recommendations.push('🚨 CREATE CHECKPOINT IMMEDIATELY'); recommendations.push('āŒ DO NOT LOAD ANY MORE FILES'); } else if (status.status === 'orange') { recommendations.push('āš ļø Consider creating checkpoint soon'); recommendations.push('šŸŽÆ Load only critical files'); } // Check for unused files const unused = []; const now = Date.now(); for (const [file, data] of this.loadedFiles) { if (now - data.loadedAt > 300000) { // 5 minutes unused.push(path.basename(file)); } } if (unused.length > 0) { recommendations.push(`šŸ’” Consider unloading unused files: ${unused.join(', ')}`); } // Check for frequently accessed files const frequent = []; for (const [file, access] of this.metrics.fileAccess) { if (access.count > 3) { frequent.push(path.basename(file)); } } if (frequent.length > 0) { recommendations.push(`šŸ“Œ Consider caching frequently accessed: ${frequent.join(', ')}`); } return recommendations; } /** * Generate report */ generateReport() { const status = this.getStatus(); const duration = Math.floor((Date.now() - this.startTime) / 1000); console.log('\n' + '='.repeat(80)); console.log('RUNTIME TOKEN TRACKING REPORT'); console.log('='.repeat(80)); // Current Status console.log('\nšŸ“Š CURRENT STATUS'); console.log('-'.repeat(40)); console.log(`Status: ${status.emoji} ${status.status.toUpperCase()} (${status.usage}%)`); console.log(`Tokens: ${status.current} / ${status.budget}`); console.log(`Remaining: ${status.remaining} tokens`); console.log(`Duration: ${duration} seconds`); // Loaded Files if (this.loadedFiles.size > 0) { console.log('\nšŸ“ LOADED FILES'); console.log('-'.repeat(40)); for (const [file, data] of this.loadedFiles) { const age = Math.floor((Date.now() - data.loadedAt) / 1000); console.log(`${path.basename(file).padEnd(30)} ${String(data.tokens).padStart(6)} tokens (${age}s ago)`); } } // Metrics console.log('\nšŸ“ˆ METRICS'); console.log('-'.repeat(40)); console.log(`Total Loaded: ${this.metrics.totalLoaded} tokens`); console.log(`Total Saved: ${this.metrics.totalSaved} tokens`); console.log(`Checkpoints: ${this.metrics.checkpoints.length}`); console.log(`Alerts: ${this.metrics.alerts.length}`); // Checkpoints if (this.metrics.checkpoints.length > 0) { console.log('\nšŸ“ CHECKPOINTS'); console.log('-'.repeat(40)); for (const cp of this.metrics.checkpoints) { console.log(`${cp.id}: ${cp.originalTokens} → ${cp.compressedTokens} tokens (${cp.compressionRatio}% saved)`); } } // Recommendations const recommendations = this.getRecommendations(); if (recommendations.length > 0) { console.log('\nšŸ’” RECOMMENDATIONS'); console.log('-'.repeat(40)); for (const rec of recommendations) { console.log(rec); } } console.log('\n' + '='.repeat(80)); } /** * Export telemetry data */ exportTelemetry() { return { session: { startTime: this.startTime, duration: Date.now() - this.startTime, budget: this.budget, peakUsage: Math.max(...this.contextHistory.map(h => h.total || 0)), checkpoints: this.checkpointCount }, metrics: this.metrics, history: this.contextHistory, fileAccess: Array.from(this.metrics.fileAccess.entries()).map(([file, access]) => ({ file: path.basename(file), ...access })) }; } } // Interactive CLI mode if (require.main === module) { const tracker = new TokenTracker({ budget: parseInt(process.argv[2]) || 5000, checkpointThreshold: parseInt(process.argv[3]) || 3000 }); console.log('šŸš€ Token Tracker Started'); console.log(`Budget: ${tracker.budget} tokens`); console.log(`Checkpoint threshold: ${tracker.checkpointThreshold} tokens`); // Simulate file loading for demo const files = [ { name: 'core-principles.md', tokens: 1500 }, { name: 'api-patterns.md', tokens: 1000 }, { name: 'security-guide.md', tokens: 800 }, { name: 'testing-strategy.md', tokens: 600 } ]; console.log('\nSimulating file loads...\n'); for (const file of files) { console.log(`Loading ${file.name}...`); tracker.loadFile(file.name, 'x'.repeat(file.tokens * 5)); const status = tracker.getStatus(); console.log(` Status: ${status.emoji} ${status.usage}% (${status.current}/${status.budget})`); // Pause for effect require('child_process').execSync('sleep 1'); } tracker.generateReport(); // Export telemetry const telemetry = tracker.exportTelemetry(); fs.writeFileSync('token-telemetry.json', JSON.stringify(telemetry, null, 2)); console.log('\nšŸ“Š Telemetry exported to token-telemetry.json'); } module.exports = TokenTracker;