bookworm-smart-assistant/hooks/constitution-session-report.js

134 lines
3.8 KiB
JavaScript
Raw Normal View History

#!/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();
}