#!/usr/bin/env node /** * Token Checkpoint Monitor * * Checks if current token usage has passed defined checkpoints * and generates pressure reports when thresholds are crossed. * * Usage: * node scripts/check-token-checkpoint.js --tokens 55000 * node scripts/check-token-checkpoint.js --tokens 55000/200000 * * This should be called AFTER each response when token count is visible * in tags. * * Copyright 2025 Tractatus Project * Licensed under Apache License 2.0 */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const TOKEN_CHECKPOINTS_PATH = path.join(__dirname, '../.claude/token-checkpoints.json'); const SESSION_STATE_PATH = path.join(__dirname, '../.claude/session-state.json'); /** * Color output */ const colors = { reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', red: '\x1b[31m', cyan: '\x1b[36m' }; function log(message, color = 'reset') { console.log(`${colors[color]}${message}${colors.reset}`); } function header(message) { console.log(''); log('═'.repeat(70), 'cyan'); log(` ${message}`, 'bright'); log('═'.repeat(70), 'cyan'); console.log(''); } function section(message) { console.log(''); log(`▶ ${message}`, 'blue'); } function success(message) { log(` ✓ ${message}`, 'green'); } function warning(message) { log(` ⚠ ${message}`, 'yellow'); } function error(message) { log(` ✗ ${message}`, 'red'); } /** * Parse token count from command line */ function parseTokenCount(arg) { if (!arg) { error('No token count provided'); console.log('Usage: node scripts/check-token-checkpoint.js --tokens 55000'); console.log(' or: node scripts/check-token-checkpoint.js --tokens 55000/200000'); process.exit(1); } // Handle both "55000" and "55000/200000" formats const parts = arg.split('/'); const current = parseInt(parts[0]); const budget = parts.length > 1 ? parseInt(parts[1]) : 200000; if (isNaN(current) || isNaN(budget)) { error(`Invalid token count: ${arg}`); process.exit(1); } return { current, budget }; } /** * Load checkpoint configuration */ function loadCheckpoints() { try { if (!fs.existsSync(TOKEN_CHECKPOINTS_PATH)) { error('Token checkpoints file not found'); error('Run: node scripts/session-init.js'); process.exit(1); } return JSON.parse(fs.readFileSync(TOKEN_CHECKPOINTS_PATH, 'utf8')); } catch (err) { error(`Failed to load checkpoints: ${err.message}`); process.exit(1); } } /** * Check if any checkpoints have been passed */ function checkCheckpoints(current, checkpoints) { const passed = []; const upcoming = []; for (const checkpoint of checkpoints.checkpoints) { if (current >= checkpoint.tokens && !checkpoint.completed) { passed.push(checkpoint); } else if (current < checkpoint.tokens) { upcoming.push(checkpoint); } } return { passed, upcoming }; } /** * Mark checkpoint as completed */ function markCheckpointCompleted(checkpoints, checkpoint) { const idx = checkpoints.checkpoints.findIndex(c => c.tokens === checkpoint.tokens); if (idx !== -1) { checkpoints.checkpoints[idx].completed = true; checkpoints.checkpoints[idx].timestamp = new Date().toISOString(); } // Update next_checkpoint to next uncompleted const nextUncompleted = checkpoints.checkpoints.find(c => !c.completed); checkpoints.next_checkpoint = nextUncompleted ? nextUncompleted.tokens : null; checkpoints.overdue = false; checkpoints.last_check = new Date().toISOString(); fs.writeFileSync(TOKEN_CHECKPOINTS_PATH, JSON.stringify(checkpoints, null, 2)); } /** * Generate pressure report for checkpoint */ function generatePressureReport(current, budget, checkpoint) { try { section(`Generating Pressure Report for ${checkpoint.percentage}% Checkpoint`); const output = execSync( `node scripts/check-session-pressure.js --tokens ${current}/${budget} --messages 1 --tasks 0`, { encoding: 'utf8', stdio: 'pipe' } ); console.log(output); return true; } catch (err) { error(`Pressure report failed: ${err.message}`); return false; } } /** * Main execution */ function main() { // Parse arguments const args = process.argv.slice(2); const tokensIdx = args.indexOf('--tokens'); if (tokensIdx === -1 || tokensIdx === args.length - 1) { error('Missing --tokens argument'); console.log('Usage: node scripts/check-token-checkpoint.js --tokens 55000'); process.exit(1); } const tokenArg = args[tokensIdx + 1]; const { current, budget } = parseTokenCount(tokenArg); header('Token Checkpoint Monitor'); log(` Current Token Usage: ${current.toLocaleString()} / ${budget.toLocaleString()}`, 'cyan'); log(` Percentage: ${((current / budget) * 100).toFixed(1)}%`, 'cyan'); // Load checkpoints const checkpoints = loadCheckpoints(); // Check which checkpoints passed const { passed, upcoming } = checkCheckpoints(current, checkpoints); if (passed.length === 0) { section('Checkpoint Status'); success('No new checkpoints passed'); if (upcoming.length > 0) { const next = upcoming[0]; const remaining = next.tokens - current; log(` Next checkpoint: ${next.tokens.toLocaleString()} tokens (${next.percentage}%)`, 'cyan'); log(` Remaining: ${remaining.toLocaleString()} tokens`, 'cyan'); } else { success('All checkpoints completed!'); } return; } // Process passed checkpoints section('Checkpoints Passed'); for (const checkpoint of passed) { warning(`Checkpoint ${checkpoint.percentage}% (${checkpoint.tokens.toLocaleString()} tokens) PASSED`); // Mark as completed markCheckpointCompleted(checkpoints, checkpoint); success(`Marked checkpoint ${checkpoint.percentage}% as completed`); // Generate pressure report generatePressureReport(current, budget, checkpoint); } // Show upcoming checkpoints if (upcoming.length > 0) { section('Upcoming Checkpoints'); for (const checkpoint of upcoming) { const remaining = checkpoint.tokens - current; log(` ${checkpoint.percentage}%: ${checkpoint.tokens.toLocaleString()} tokens (${remaining.toLocaleString()} remaining)`, 'cyan'); } } console.log(''); log('═'.repeat(70), 'cyan'); success('Checkpoint monitoring complete'); log('═'.repeat(70), 'cyan'); console.log(''); } // Run if called directly if (require.main === module) { main(); } module.exports = { parseTokenCount, loadCheckpoints, checkCheckpoints };