UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

341 lines (340 loc) 12.3 kB
#!/usr/bin/env npx ts-node import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); import { readFileSync, existsSync, readdirSync, statSync } from "fs"; import { join } from "path"; import { homedir } from "os"; import Database from "better-sqlite3"; function estimateTokensAccurate(text) { const baseEstimate = text.length / 3.5; const codeIndicators = (text.match(/[{}\[\]();=]/g) || []).length; const codeScore = codeIndicators / text.length * 100; if (codeScore > 5) { return Math.ceil(baseEstimate * 1.2); } return Math.ceil(baseEstimate); } function measureHandoffs() { const handoffPath = join(homedir(), ".stackmemory", "context.db"); const metrics = []; if (!existsSync(handoffPath)) { console.log("No context.db found at", handoffPath); return metrics; } try { const db = new Database(handoffPath, { readonly: true }); const tableCheck = db.prepare( ` SELECT name FROM sqlite_master WHERE type='table' AND name='handoff_requests' ` ).get(); if (!tableCheck) { console.log("No handoff_requests table found"); db.close(); return metrics; } const handoffs = db.prepare( ` SELECT id, message, created_at FROM handoff_requests ORDER BY created_at DESC LIMIT 10 ` ).all(); for (const h of handoffs) { const message = h.message || ""; metrics.push({ handoffId: h.id, handoffChars: message.length, handoffTokens: estimateTokensAccurate(message), createdAt: new Date(h.created_at).toISOString() }); } db.close(); } catch (err) { console.log("Error reading handoffs:", err); } return metrics; } function measureLastHandoffFile() { const handoffPath = join(process.cwd(), ".stackmemory", "last-handoff.md"); if (!existsSync(handoffPath)) { const homeHandoff = join(homedir(), ".stackmemory", "last-handoff.md"); if (!existsSync(homeHandoff)) { return null; } const content2 = readFileSync(homeHandoff, "utf-8"); return { source: homeHandoff, charCount: content2.length, estimatedTokens: estimateTokensAccurate(content2), lineCount: content2.split("\n").length }; } const content = readFileSync(handoffPath, "utf-8"); return { source: handoffPath, charCount: content.length, estimatedTokens: estimateTokensAccurate(content), lineCount: content.split("\n").length }; } function measureClaudeConversations() { const claudeProjectsDir = join(homedir(), ".claude", "projects"); const metrics = []; if (!existsSync(claudeProjectsDir)) { return metrics; } const projectDirs = readdirSync(claudeProjectsDir); for (const dir of projectDirs.slice(0, 5)) { const projectPath = join(claudeProjectsDir, dir); const stat = statSync(projectPath); if (stat.isDirectory()) { const files = readdirSync(projectPath).filter( (f) => f.endsWith(".jsonl") ); for (const file of files.slice(0, 3)) { const filePath = join(projectPath, file); try { const content = readFileSync(filePath, "utf-8"); metrics.push({ source: file, charCount: content.length, estimatedTokens: estimateTokensAccurate(content), lineCount: content.split("\n").length }); } catch { } } } } return metrics; } function measureFramesAndEvents() { const dbPath = join(homedir(), ".stackmemory", "context.db"); if (!existsSync(dbPath)) { return null; } try { const db = new Database(dbPath, { readonly: true }); const frameResult = db.prepare( ` SELECT COUNT(*) as count, SUM(LENGTH(COALESCE(name, '') || COALESCE(json(inputs), '') || COALESCE(json(outputs), '') || COALESCE(json(digest_json), ''))) as totalChars FROM frames ` ).get(); const eventResult = db.prepare( ` SELECT COUNT(*) as count, SUM(LENGTH(COALESCE(event_type, '') || COALESCE(json(payload), ''))) as totalChars FROM events ` ).get(); db.close(); const frameChars = frameResult?.totalChars || 0; const eventChars = eventResult?.totalChars || 0; const totalChars = frameChars + eventChars; return { sessionId: "aggregate", frameCount: frameResult?.count || 0, eventCount: eventResult?.count || 0, estimatedSessionTokens: estimateTokensAccurate( String(totalChars).repeat(Math.floor(totalChars / 10) || 1) ) }; } catch (err) { console.log("Error measuring frames/events:", err); return null; } } function formatNumber(n) { if (n >= 1e3) { return (n / 1e3).toFixed(1) + "K"; } return n.toString(); } async function main() { console.log("========================================"); console.log(" HANDOFF CONTEXT IMPACT ANALYSIS"); console.log(" (Actual Measurements)"); console.log("========================================\n"); console.log("1. LAST HANDOFF FILE"); console.log("--------------------"); const lastHandoff = measureLastHandoffFile(); if (lastHandoff) { console.log(` Source: ${lastHandoff.source}`); console.log(` Characters: ${formatNumber(lastHandoff.charCount)}`); console.log(` Lines: ${lastHandoff.lineCount}`); console.log( ` Estimated tokens: ${formatNumber(lastHandoff.estimatedTokens)}` ); } else { console.log(" No handoff file found"); } console.log(""); console.log("2. HANDOFFS FROM DATABASE"); console.log("-------------------------"); const handoffs = measureHandoffs(); if (handoffs.length > 0) { let totalTokens = 0; for (const h of handoffs) { console.log( ` ${h.handoffId.slice(0, 8)}: ${formatNumber(h.handoffTokens)} tokens (${formatNumber(h.handoffChars)} chars)` ); totalTokens += h.handoffTokens; } const avgTokens = Math.round(totalTokens / handoffs.length); console.log(` Average: ${formatNumber(avgTokens)} tokens per handoff`); } else { console.log(" No handoffs in database"); } console.log(""); console.log("3. CLAUDE CONVERSATION FILES"); console.log("----------------------------"); const conversations = measureClaudeConversations(); if (conversations.length > 0) { let totalConvTokens = 0; let maxConvTokens = 0; for (const c of conversations) { console.log( ` ${c.source}: ${formatNumber(c.estimatedTokens)} tokens (${formatNumber(c.charCount)} chars, ${c.lineCount} lines)` ); totalConvTokens += c.estimatedTokens; maxConvTokens = Math.max(maxConvTokens, c.estimatedTokens); } const avgConvTokens = Math.round(totalConvTokens / conversations.length); console.log( ` Average: ${formatNumber(avgConvTokens)} tokens per conversation` ); console.log(` Max: ${formatNumber(maxConvTokens)} tokens`); } else { console.log(" No conversation files found"); } console.log(""); console.log("4. STACKMEMORY DATABASE CONTENT"); console.log("-------------------------------"); const dbMetrics = measureFramesAndEvents(); if (dbMetrics) { console.log(` Frames: ${dbMetrics.frameCount}`); console.log(` Events: ${dbMetrics.eventCount}`); console.log( ` Total stored data: ~${formatNumber(dbMetrics.estimatedSessionTokens)} tokens equivalent` ); } else { console.log(" No database metrics available"); } console.log(""); console.log("5. COMPRESSION ANALYSIS"); console.log("-----------------------"); const avgHandoffTokens = handoffs.length > 0 ? Math.round( handoffs.reduce((sum, h) => sum + h.handoffTokens, 0) / handoffs.length ) : lastHandoff?.estimatedTokens || 2e3; const avgConversationTokens = conversations.length > 0 ? Math.round( conversations.reduce((sum, c) => sum + c.estimatedTokens, 0) / conversations.length ) : 8e4; const sessionSizes = { short: 35e3, // 2hr session medium: 78e3, // 4hr session long: 142e3, // 8hr session actual: avgConversationTokens }; console.log("\n Compression Ratios (using actual handoff size):"); console.log(` Handoff size: ${formatNumber(avgHandoffTokens)} tokens `); for (const [label, size] of Object.entries(sessionSizes)) { const reduction = ((size - avgHandoffTokens) / size * 100).toFixed(1); const saved = size - avgHandoffTokens; console.log( ` ${label.padEnd(8)}: ${formatNumber(size)} -> ${formatNumber(avgHandoffTokens)} = ${reduction}% reduction (${formatNumber(saved)} saved)` ); } console.log(""); console.log("6. CONTEXT WINDOW IMPACT"); console.log("------------------------"); const contextWindow = 2e5; const systemPrompt = 2e3; const currentTools = 1e4; const withoutHandoff = { used: systemPrompt + avgConversationTokens + currentTools, available: 0 }; withoutHandoff.available = contextWindow - withoutHandoff.used; const withHandoff = { used: systemPrompt + avgHandoffTokens + currentTools, available: 0 }; withHandoff.available = contextWindow - withHandoff.used; console.log(` Context window: ${formatNumber(contextWindow)} tokens`); console.log(` System prompt: ${formatNumber(systemPrompt)} tokens`); console.log(` Current tools: ${formatNumber(currentTools)} tokens `); console.log(" WITHOUT HANDOFF:"); console.log( ` Conversation history: ${formatNumber(avgConversationTokens)} tokens` ); console.log(` Total used: ${formatNumber(withoutHandoff.used)} tokens`); console.log( ` Available for work: ${formatNumber(withoutHandoff.available)} tokens (${(withoutHandoff.available / contextWindow * 100).toFixed(1)}%)` ); console.log(""); console.log(" WITH HANDOFF:"); console.log(` Handoff summary: ${formatNumber(avgHandoffTokens)} tokens`); console.log(` Total used: ${formatNumber(withHandoff.used)} tokens`); console.log( ` Available for work: ${formatNumber(withHandoff.available)} tokens (${(withHandoff.available / contextWindow * 100).toFixed(1)}%)` ); console.log(""); const improvement = withHandoff.available - withoutHandoff.available; const improvementPct = (improvement / withoutHandoff.available * 100).toFixed(1); console.log( ` IMPROVEMENT: +${formatNumber(improvement)} tokens (+${improvementPct}% more capacity)` ); console.log("\n========================================"); console.log(" SUMMARY"); console.log("========================================\n"); const actualReduction = ((avgConversationTokens - avgHandoffTokens) / avgConversationTokens * 100).toFixed(1); console.log( ` Actual handoff size: ${formatNumber(avgHandoffTokens)} tokens` ); console.log( ` Actual conversation size: ${formatNumber(avgConversationTokens)} tokens` ); console.log(` Actual compression: ${actualReduction}%`); console.log(` Actual context freed: ${formatNumber(improvement)} tokens`); console.log(""); console.log(" CLAIM VALIDATION:"); console.log(" -----------------"); const claimedReduction = "85-98%"; const claimedHandoff = "1K-5K tokens"; const claimedConversation = "50K-150K tokens"; console.log(` Claimed reduction: ${claimedReduction}`); console.log(` Measured reduction: ${actualReduction}%`); console.log( ` Status: ${parseFloat(actualReduction) >= 85 ? "VALIDATED" : "NEEDS REVISION"}` ); console.log(""); console.log(` Claimed handoff size: ${claimedHandoff}`); console.log( ` Measured handoff size: ${formatNumber(avgHandoffTokens)} tokens` ); console.log( ` Status: ${avgHandoffTokens >= 1e3 && avgHandoffTokens <= 5e3 ? "VALIDATED" : "NEEDS REVISION"}` ); console.log(""); console.log(` Claimed conversation: ${claimedConversation}`); console.log( ` Measured conversation: ${formatNumber(avgConversationTokens)} tokens` ); console.log( ` Status: ${avgConversationTokens >= 5e4 && avgConversationTokens <= 15e4 ? "VALIDATED" : "NEEDS REVISION"}` ); } main().catch(console.error); //# sourceMappingURL=measure-handoff-impact.js.map