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

134 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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