134 lines
3.8 KiB
JavaScript
134 lines
3.8 KiB
JavaScript
|
|
#!/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();
|
|||
|
|
}
|