#!/usr/bin/env node /** * Stop Hook 追加: 宪法违规会话摘要 * 读取 debug/detection-stats.json 中当天的违规统计 * 输出到 debug/constitution-report.jsonl * * Fail-open: 异常 → exit(0) */ const fs = require('fs'); const path = require('path'); const { safeAppendJsonl } = require('./lib/safe-append.js'); function main() { try { const root = require('./lib/root.js'); const debugDir = path.join(root, 'debug'); const statsFile = path.join(debugDir, 'detection-stats.json'); const reportFile = path.join(debugDir, 'constitution-report.jsonl'); const reminderFile = path.join(debugDir, 'constitution-reminder-session.json'); const today = new Date().toISOString().slice(0, 10); let todayViolations = 0; let ruleBreakdown = {}; // 从 detection-stats.json 读取当日统计 if (fs.existsSync(statsFile)) { try { const stats = JSON.parse(fs.readFileSync(statsFile, 'utf8')); todayViolations = (stats.dailyTotals || {})[today] || 0; for (const [ruleId, ruleData] of Object.entries(stats.rules || {})) { const todayCount = (ruleData.last7days || {})[today] || 0; if (todayCount > 0) { ruleBreakdown[ruleId] = todayCount; } } } catch {} } // 从 constitution-reminder-session.json 读取提醒统计 let filesReminded = 0; if (fs.existsSync(reminderFile)) { try { const session = JSON.parse(fs.readFileSync(reminderFile, 'utf8')); filesReminded = (session.reminded || []).length; } catch {} } // 如果今天有违规或提醒,记录到报告 if (todayViolations > 0 || filesReminded > 0) { const entry = { ts: new Date().toISOString(), date: today, violations: todayViolations, ruleBreakdown, filesReminded, }; safeAppendJsonl(reportFile, entry); } // 清理提醒会话文件 (Stop 时重置) if (fs.existsSync(reminderFile)) { try { fs.unlinkSync(reminderFile); } catch {} } } catch {} process.exit(0); } /** * 可导出的会话报告生成函数 (供 dispatcher 调用) * 不调用 process.exit,直接执行逻辑 */ function runReport() { try { const root = require('./lib/root.js'); const debugDir = path.join(root, 'debug'); const statsFile = path.join(debugDir, 'detection-stats.json'); const reportFile = path.join(debugDir, 'constitution-report.jsonl'); const reminderFile = path.join(debugDir, 'constitution-reminder-session.json'); const today = new Date().toISOString().slice(0, 10); let todayViolations = 0; let ruleBreakdown = {}; if (fs.existsSync(statsFile)) { try { const stats = JSON.parse(fs.readFileSync(statsFile, 'utf8')); todayViolations = (stats.dailyTotals || {})[today] || 0; for (const [ruleId, ruleData] of Object.entries(stats.rules || {})) { const todayCount = (ruleData.last7days || {})[today] || 0; if (todayCount > 0) { ruleBreakdown[ruleId] = todayCount; } } } catch {} } let filesReminded = 0; if (fs.existsSync(reminderFile)) { try { const session = JSON.parse(fs.readFileSync(reminderFile, 'utf8')); filesReminded = (session.reminded || []).length; } catch {} } if (todayViolations > 0 || filesReminded > 0) { const entry = { ts: new Date().toISOString(), date: today, violations: todayViolations, ruleBreakdown, filesReminded, }; safeAppendJsonl(reportFile, entry); } if (fs.existsSync(reminderFile)) { try { fs.unlinkSync(reminderFile); } catch {} } } catch {} } // 模块导出 (供 dispatcher 和测试使用) if (typeof module !== 'undefined') { module.exports = { runReport }; } if (require.main === module) { main(); }