- Add --tokens=<current>/<budget> parameter to framework-stats.js - Calculate and display real-time context pressure when tokens provided - Show data source (real-time/cached/history/stats) in output - Display warning when showing cached data - Include token budget and usage percentage in output Fixes issue where ffs showed stale 0% pressure from cached session state 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
443 lines
16 KiB
JavaScript
Executable file
443 lines
16 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Framework Statistics Reporter
|
|
* Displays comprehensive framework statistics for current session
|
|
*
|
|
* Triggered by "ffs" (Full Framework Stats) codeword
|
|
*
|
|
* Reports:
|
|
* - Session context pressure and token usage
|
|
* - Framework service invocation counts
|
|
* - Recent audit log entries (last 10)
|
|
* - Active instruction statistics
|
|
* - Service health/initialization status
|
|
*/
|
|
|
|
const mongoose = require('mongoose');
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
// Load environment
|
|
require('dotenv').config();
|
|
|
|
// Framework services
|
|
const BoundaryEnforcer = require('../src/services/BoundaryEnforcer.service');
|
|
const MetacognitiveVerifier = require('../src/services/MetacognitiveVerifier.service');
|
|
const ContextPressureMonitor = require('../src/services/ContextPressureMonitor.service');
|
|
const CrossReferenceValidator = require('../src/services/CrossReferenceValidator.service');
|
|
const InstructionPersistenceClassifier = require('../src/services/InstructionPersistenceClassifier.service');
|
|
const PluralisticDeliberationOrchestrator = require('../src/services/PluralisticDeliberationOrchestrator.service');
|
|
|
|
async function getSessionStats() {
|
|
const sessionStatePath = path.join(__dirname, '../.claude/session-state.json');
|
|
if (fs.existsSync(sessionStatePath)) {
|
|
const sessionState = JSON.parse(fs.readFileSync(sessionStatePath, 'utf8'));
|
|
|
|
// Extract pressure monitoring data
|
|
const pressureData = sessionState.last_framework_activity?.ContextPressureMonitor;
|
|
|
|
// Extract auto-compact events
|
|
const autoCompacts = sessionState.auto_compact_events || [];
|
|
|
|
// Extract component statistics
|
|
const componentStats = sessionState.framework_components || {};
|
|
|
|
return {
|
|
...sessionState,
|
|
pressureMonitoring: pressureData,
|
|
autoCompactEvents: autoCompacts,
|
|
componentStatistics: componentStats
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function getTokenCheckpoints() {
|
|
const checkpointPath = path.join(__dirname, '../.claude/token-checkpoints.json');
|
|
if (fs.existsSync(checkpointPath)) {
|
|
return JSON.parse(fs.readFileSync(checkpointPath, 'utf8'));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function getInstructionStats() {
|
|
const instructionPath = path.join(__dirname, '../.claude/instruction-history.json');
|
|
if (fs.existsSync(instructionPath)) {
|
|
const history = JSON.parse(fs.readFileSync(instructionPath, 'utf8'));
|
|
|
|
const active = history.instructions.filter(i => i.active);
|
|
const byQuadrant = active.reduce((acc, i) => {
|
|
acc[i.quadrant] = (acc[i.quadrant] || 0) + 1;
|
|
return acc;
|
|
}, {});
|
|
|
|
const byPersistence = active.reduce((acc, i) => {
|
|
acc[i.persistence] = (acc[i.persistence] || 0) + 1;
|
|
return acc;
|
|
}, {});
|
|
|
|
return {
|
|
total: history.instructions.length,
|
|
active: active.length,
|
|
inactive: history.instructions.length - active.length,
|
|
byQuadrant,
|
|
byPersistence,
|
|
version: history.version,
|
|
lastUpdated: history.last_updated
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
async function getAuditLogStats() {
|
|
try {
|
|
const db = require('../src/utils/db.util');
|
|
const collection = await db.getCollection('auditLogs');
|
|
|
|
// Get total count
|
|
const total = await collection.countDocuments();
|
|
|
|
// Get counts by service
|
|
const byService = await collection.aggregate([
|
|
{ $group: { _id: '$service', count: { $sum: 1 } } },
|
|
{ $sort: { count: -1 } }
|
|
]).toArray();
|
|
|
|
// Get recent entries (last 10)
|
|
const recent = await collection.find()
|
|
.sort({ timestamp: -1 })
|
|
.limit(10)
|
|
.toArray();
|
|
|
|
// Get today's count
|
|
const todayStart = new Date();
|
|
todayStart.setHours(0, 0, 0, 0);
|
|
const todayCount = await collection.countDocuments({
|
|
timestamp: { $gte: todayStart }
|
|
});
|
|
|
|
return {
|
|
total,
|
|
todayCount,
|
|
byService: byService.reduce((acc, s) => {
|
|
acc[s._id] = s.count;
|
|
return acc;
|
|
}, {}),
|
|
recent: recent.map(r => ({
|
|
service: r.service,
|
|
action: r.action,
|
|
decision: r.decision,
|
|
timestamp: r.timestamp,
|
|
sessionId: r.context?.sessionId
|
|
}))
|
|
};
|
|
} catch (error) {
|
|
console.error('Error fetching audit logs:', error.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
try {
|
|
// Parse command-line arguments
|
|
const args = process.argv.slice(2);
|
|
const tokensArg = args.find(arg => arg.startsWith('--tokens='));
|
|
let currentTokens = null;
|
|
let tokenBudget = null;
|
|
|
|
if (tokensArg) {
|
|
const tokensValue = tokensArg.split('=')[1];
|
|
const [current, budget] = tokensValue.split('/').map(t => parseInt(t.trim()));
|
|
if (!isNaN(current) && !isNaN(budget)) {
|
|
currentTokens = current;
|
|
tokenBudget = budget;
|
|
}
|
|
}
|
|
|
|
// Connect to MongoDB
|
|
await mongoose.connect('mongodb://localhost:27017/tractatus_dev', {
|
|
serverSelectionTimeoutMS: 2000
|
|
});
|
|
|
|
// Initialize framework services
|
|
await BoundaryEnforcer.initialize();
|
|
await MetacognitiveVerifier.initialize();
|
|
await ContextPressureMonitor.initialize('framework-stats');
|
|
await CrossReferenceValidator.initialize();
|
|
await InstructionPersistenceClassifier.initialize();
|
|
await PluralisticDeliberationOrchestrator.initialize();
|
|
|
|
// Gather statistics
|
|
const [sessionStats, tokenCheckpoints, instructionStats, auditStats] = await Promise.all([
|
|
getSessionStats(),
|
|
getTokenCheckpoints(),
|
|
getInstructionStats(),
|
|
getAuditLogStats()
|
|
]);
|
|
|
|
// Get current pressure stats
|
|
const monitorStats = ContextPressureMonitor.getStats();
|
|
const pressureHistory = ContextPressureMonitor.getPressureHistory();
|
|
const latestPressure = pressureHistory && pressureHistory.length > 0
|
|
? pressureHistory[pressureHistory.length - 1]
|
|
: null;
|
|
|
|
// Calculate real-time pressure if tokens provided
|
|
let realTimePressure = null;
|
|
if (currentTokens !== null && tokenBudget !== null) {
|
|
realTimePressure = await ContextPressureMonitor.analyzePressure(currentTokens, tokenBudget, 1);
|
|
}
|
|
|
|
// Build report
|
|
const report = {
|
|
timestamp: new Date().toISOString(),
|
|
session: sessionStats ? {
|
|
sessionId: sessionStats.session_id,
|
|
startTime: sessionStats.started,
|
|
messageCount: sessionStats.message_count,
|
|
tokenEstimate: sessionStats.token_estimate,
|
|
actionCount: sessionStats.action_count,
|
|
lastUpdated: sessionStats.last_updated,
|
|
initialized: sessionStats.initialized
|
|
} : null,
|
|
|
|
tokenUsage: tokenCheckpoints ? {
|
|
budget: tokenCheckpoints.tokenBudget,
|
|
checkpoints: tokenCheckpoints.checkpoints,
|
|
nextCheckpoint: tokenCheckpoints.checkpoints.find(c => !c.reached)
|
|
} : null,
|
|
|
|
contextPressure: realTimePressure ? {
|
|
level: realTimePressure.pressureLevel?.name || 'NORMAL',
|
|
score: Math.round(realTimePressure.overallScore * 10) / 10,
|
|
tokenCount: currentTokens,
|
|
tokenBudget: tokenBudget,
|
|
source: 'real-time',
|
|
timestamp: new Date().toISOString(),
|
|
metrics: realTimePressure.metrics
|
|
} : (sessionStats?.pressureMonitoring ? {
|
|
level: sessionStats.pressureMonitoring.last_level || 'UNKNOWN',
|
|
score: sessionStats.pressureMonitoring.last_score || 0,
|
|
lastChecked: sessionStats.pressureMonitoring.timestamp,
|
|
messageNumber: sessionStats.pressureMonitoring.message,
|
|
tokenCount: sessionStats.pressureMonitoring.tokens,
|
|
source: 'cached'
|
|
} : (latestPressure ? {
|
|
level: latestPressure.pressureLevel?.name || 'UNKNOWN',
|
|
score: latestPressure.overallScore,
|
|
timestamp: latestPressure.timestamp,
|
|
metrics: latestPressure.metrics,
|
|
source: 'history'
|
|
} : (monitorStats ? {
|
|
level: 'UNKNOWN',
|
|
score: 0,
|
|
stats: monitorStats,
|
|
source: 'stats'
|
|
} : null))),
|
|
|
|
autoCompacts: sessionStats?.autoCompactEvents ? {
|
|
total: sessionStats.autoCompactEvents.length,
|
|
events: sessionStats.autoCompactEvents.map(e => ({
|
|
timestamp: e.timestamp,
|
|
beforeTokens: e.before_tokens,
|
|
afterTokens: e.after_tokens,
|
|
reduction: e.reduction_percent,
|
|
messageNumber: e.message_number,
|
|
pressureBefore: e.pressure_before,
|
|
pressureAfter: e.pressure_after
|
|
}))
|
|
} : { total: 0, events: [] },
|
|
|
|
componentStats: sessionStats?.componentStatistics ? Object.entries(sessionStats.componentStatistics).map(([name, stats]) => ({
|
|
name,
|
|
validations: stats.validations_performed,
|
|
blocks: stats.blocks_issued,
|
|
lastActivity: stats.last_validation,
|
|
tokenCount: stats.tokens
|
|
})) : [],
|
|
|
|
instructions: instructionStats,
|
|
|
|
auditLogs: auditStats ? {
|
|
total: auditStats.total,
|
|
today: auditStats.todayCount,
|
|
byService: auditStats.byService,
|
|
recentCount: auditStats.recent.length
|
|
} : null,
|
|
|
|
frameworkServices: {
|
|
BoundaryEnforcer: 'ACTIVE',
|
|
MetacognitiveVerifier: 'ACTIVE',
|
|
ContextPressureMonitor: 'ACTIVE',
|
|
CrossReferenceValidator: 'ACTIVE',
|
|
InstructionPersistenceClassifier: 'ACTIVE',
|
|
PluralisticDeliberationOrchestrator: 'ACTIVE'
|
|
}
|
|
};
|
|
|
|
// Output formatted report
|
|
console.log('\n╔════════════════════════════════════════════════════════════════╗');
|
|
console.log('║ TRACTATUS FRAMEWORK STATISTICS (ffs) ║');
|
|
console.log('╚════════════════════════════════════════════════════════════════╝\n');
|
|
|
|
// Session Info
|
|
if (report.session) {
|
|
console.log('📊 SESSION');
|
|
console.log(` Session ID: ${report.session.sessionId}`);
|
|
console.log(` Start Time: ${new Date(report.session.startTime).toLocaleString()}`);
|
|
console.log(` Message Count: ${report.session.messageCount}`);
|
|
console.log(` Token Estimate: ${report.session.tokenEstimate?.toLocaleString() || 'N/A'}`);
|
|
console.log(` Action Count: ${report.session.actionCount || 0}`);
|
|
console.log(` Last Updated: ${new Date(report.session.lastUpdated).toLocaleString()}`);
|
|
console.log(` Initialized: ${report.session.initialized ? 'Yes' : 'No'}`);
|
|
console.log();
|
|
}
|
|
|
|
// Token Usage
|
|
if (report.tokenUsage && report.tokenUsage.budget) {
|
|
const next = report.tokenUsage.nextCheckpoint;
|
|
console.log('🎯 TOKEN BUDGET');
|
|
console.log(` Total Budget: ${report.tokenUsage.budget.toLocaleString()}`);
|
|
if (next) {
|
|
console.log(` Next Checkpoint: ${next.threshold.toLocaleString()} (${next.percentage}%)`);
|
|
}
|
|
const reached = report.tokenUsage.checkpoints.filter(c => c.reached).length;
|
|
console.log(` Checkpoints: ${reached}/${report.tokenUsage.checkpoints.length} reached`);
|
|
console.log();
|
|
}
|
|
|
|
// Context Pressure
|
|
if (report.contextPressure) {
|
|
console.log('⚠️ CONTEXT PRESSURE');
|
|
console.log(` Level: ${report.contextPressure.level}`);
|
|
console.log(` Overall Score: ${report.contextPressure.score}%`);
|
|
console.log(` Data Source: ${report.contextPressure.source || 'unknown'}`);
|
|
|
|
if (report.contextPressure.source === 'cached') {
|
|
console.log(' ⚠️ WARNING: Showing cached data. Use --tokens=<current>/<budget> for real-time.');
|
|
}
|
|
|
|
if (report.contextPressure.tokenCount) {
|
|
console.log(` Token Count: ${report.contextPressure.tokenCount?.toLocaleString()}`);
|
|
}
|
|
if (report.contextPressure.tokenBudget) {
|
|
console.log(` Token Budget: ${report.contextPressure.tokenBudget?.toLocaleString()}`);
|
|
const percentUsed = ((report.contextPressure.tokenCount / report.contextPressure.tokenBudget) * 100).toFixed(1);
|
|
console.log(` Tokens Used: ${percentUsed}%`);
|
|
}
|
|
if (report.contextPressure.lastChecked) {
|
|
console.log(` Last Checked: ${new Date(report.contextPressure.lastChecked).toLocaleString()}`);
|
|
console.log(` At Message: #${report.contextPressure.messageNumber}`);
|
|
}
|
|
if (report.contextPressure.timestamp && report.contextPressure.source !== 'cached') {
|
|
console.log(` Calculated: ${new Date(report.contextPressure.timestamp).toLocaleString()}`);
|
|
}
|
|
if (report.contextPressure.metrics) {
|
|
console.log(' Metrics:');
|
|
Object.entries(report.contextPressure.metrics).forEach(([metric, data]) => {
|
|
if (data && typeof data === 'object' && 'normalized' in data) {
|
|
console.log(` • ${metric}: ${(data.normalized * 100).toFixed(1)}%`);
|
|
}
|
|
});
|
|
}
|
|
if (report.contextPressure.stats) {
|
|
console.log(' Analysis Count:', report.contextPressure.stats.analysis_count);
|
|
}
|
|
console.log();
|
|
}
|
|
|
|
// Auto-Compact Events
|
|
if (report.autoCompacts) {
|
|
console.log('🔄 AUTO-COMPACT EVENTS');
|
|
console.log(` Total Compactions: ${report.autoCompacts.total}`);
|
|
if (report.autoCompacts.total > 0) {
|
|
console.log('\n Recent Compactions:');
|
|
report.autoCompacts.events.slice(-5).forEach((event, idx) => {
|
|
console.log(`\n ${idx + 1}. ${new Date(event.timestamp).toLocaleString()}`);
|
|
console.log(` Message #${event.messageNumber}`);
|
|
console.log(` Tokens: ${event.beforeTokens?.toLocaleString()} → ${event.afterTokens?.toLocaleString()} (${event.reduction}% reduction)`);
|
|
if (event.pressureBefore) {
|
|
console.log(` Pressure: ${event.pressureBefore} → ${event.pressureAfter}`);
|
|
}
|
|
});
|
|
} else {
|
|
console.log(' No auto-compaction events recorded yet.');
|
|
console.log(' (Framework will log compactions as they occur)');
|
|
}
|
|
console.log();
|
|
}
|
|
|
|
// Component Statistics
|
|
if (report.componentStats && report.componentStats.length > 0) {
|
|
console.log('🔧 COMPONENT STATISTICS');
|
|
report.componentStats.forEach(comp => {
|
|
console.log(`\n ${comp.name}:`);
|
|
console.log(` Validations: ${comp.validations?.toLocaleString() || 'N/A'}`);
|
|
if (comp.blocks !== undefined) {
|
|
console.log(` Blocks Issued: ${comp.blocks}`);
|
|
}
|
|
if (comp.lastActivity) {
|
|
console.log(` Last Active: ${new Date(comp.lastActivity).toLocaleString()}`);
|
|
}
|
|
});
|
|
console.log();
|
|
}
|
|
|
|
// Instruction Stats
|
|
if (report.instructions) {
|
|
console.log('📋 INSTRUCTIONS');
|
|
console.log(` Total: ${report.instructions.total}`);
|
|
console.log(` Active: ${report.instructions.active}`);
|
|
console.log(` Inactive: ${report.instructions.inactive}`);
|
|
console.log(` Version: ${report.instructions.version}`);
|
|
console.log(' By Quadrant:');
|
|
Object.entries(report.instructions.byQuadrant).forEach(([quad, count]) => {
|
|
console.log(` • ${quad}: ${count}`);
|
|
});
|
|
console.log(' By Persistence:');
|
|
Object.entries(report.instructions.byPersistence).forEach(([pers, count]) => {
|
|
console.log(` • ${pers}: ${count}`);
|
|
});
|
|
console.log();
|
|
}
|
|
|
|
// Audit Logs
|
|
if (report.auditLogs) {
|
|
console.log('📝 AUDIT LOGS');
|
|
console.log(` Total Decisions: ${report.auditLogs.total}`);
|
|
console.log(` Today: ${report.auditLogs.today}`);
|
|
console.log(' By Service:');
|
|
Object.entries(report.auditLogs.byService)
|
|
.sort((a, b) => b[1] - a[1])
|
|
.forEach(([service, count]) => {
|
|
console.log(` • ${service}: ${count}`);
|
|
});
|
|
console.log();
|
|
}
|
|
|
|
// Framework Services
|
|
console.log('🔧 FRAMEWORK SERVICES');
|
|
Object.entries(report.frameworkServices).forEach(([service, status]) => {
|
|
const icon = status === 'ACTIVE' ? '✓' : '✗';
|
|
console.log(` ${icon} ${service}: ${status}`);
|
|
});
|
|
console.log();
|
|
|
|
console.log('╚════════════════════════════════════════════════════════════════╝\n');
|
|
|
|
// Also output JSON for programmatic access
|
|
console.log('\n// JSON OUTPUT FOR PROGRAMMATIC ACCESS:');
|
|
console.log(JSON.stringify(report, null, 2));
|
|
|
|
await mongoose.disconnect();
|
|
process.exit(0);
|
|
|
|
} catch (error) {
|
|
console.error('Error generating framework statistics:', error);
|
|
await mongoose.disconnect();
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
main();
|