import fs from 'fs/promises'; import crypto from 'crypto'; import path from 'path'; import glob from 'fast-glob'; /** * Rigstate ContextHasher: Generates cryptographic signatures for architectural modules. * Used for drift detection and "Digital Sealing" of verified states. */ export async function generateContextHashes(cwd: string, patterns: string[]): Promise> { const hashes: Record = {}; const files = await glob(patterns, { cwd, absolute: true, onlyFiles: true, ignore: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/.next/**'] }); for (const file of files) { try { const content = await fs.readFile(file, 'utf-8'); const relativePath = path.relative(cwd, file); // Normalize line endings and whitespace for more robust hashing const normalizedContent = content.replace(/\r\n/g, '\n').trim(); const hash = crypto.createHash('sha256').update(normalizedContent).digest('hex'); hashes[relativePath] = hash; } catch (error) { console.warn(`[Hasher] Could not read file ${file}:`, error); } } return hashes; } /** * Verifies local drift against a previously accepted baseline. */ export function detectDrift(currentHashes: Record, baselineHashes: Record) { const drift: { file: string; type: 'MODIFIED' | 'MISSING' | 'NEW' }[] = []; // Check for modifications and missing files for (const [file, hash] of Object.entries(baselineHashes)) { if (!currentHashes[file]) { drift.push({ file, type: 'MISSING' }); } else if (currentHashes[file] !== hash) { drift.push({ file, type: 'MODIFIED' }); } } // Check for new files (optional, maybe not relevant for architectural baseline) for (const file of Object.keys(currentHashes)) { if (!baselineHashes[file]) { drift.push({ file, type: 'NEW' }); } } return drift; }