146 lines
5.0 KiB
JavaScript
146 lines
5.0 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
// scripts/generate-session-summary.js
|
|||
|
|
// 铸渊 · 会话摘要生成器
|
|||
|
|
//
|
|||
|
|
// 在 Notion Agent 巡检前 10 分钟自动运行,生成 latest-summary.json
|
|||
|
|
// 供 Notion 侧「铸渊·桥接巡检引擎」读取
|
|||
|
|
//
|
|||
|
|
// 用法:node scripts/generate-session-summary.js
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const ROOT = path.resolve(__dirname, '..');
|
|||
|
|
const TELEMETRY_DIR = path.join(ROOT, 'persona-telemetry');
|
|||
|
|
const SUMMARY_FILE = path.join(TELEMETRY_DIR, 'latest-summary.json');
|
|||
|
|
const SYSLOG_INBOX = path.join(ROOT, 'syslog-inbox');
|
|||
|
|
const DEV_STATUS = path.join(ROOT, '.github', 'persona-brain', 'dev-status.json');
|
|||
|
|
const STYLE_CONFIG = path.join(ROOT, '.github', 'persona-brain', 'style-config.json');
|
|||
|
|
const MEMORY_FILE = path.join(ROOT, '.github', 'persona-brain', 'memory.json');
|
|||
|
|
const TUNING_QUEUE = path.join(TELEMETRY_DIR, 'tuning-queue');
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
// 辅助函数
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
function safeReadJSON(filePath) {
|
|||
|
|
try {
|
|||
|
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|||
|
|
} catch {
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function countFiles(dir, ext) {
|
|||
|
|
if (!fs.existsSync(dir)) return 0;
|
|||
|
|
return fs.readdirSync(dir).filter(f => {
|
|||
|
|
if (ext) return f.endsWith(ext);
|
|||
|
|
return !f.startsWith('.') && f !== 'README.md';
|
|||
|
|
}).length;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function countPendingTuningOrders() {
|
|||
|
|
if (!fs.existsSync(TUNING_QUEUE)) return 0;
|
|||
|
|
return fs.readdirSync(TUNING_QUEUE).filter(f =>
|
|||
|
|
f.startsWith('TUNE-') && f.endsWith('.json')
|
|||
|
|
).length;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
// 主流程
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
function generateSummary() {
|
|||
|
|
const now = new Date().toISOString();
|
|||
|
|
|
|||
|
|
// 读取开发者状态
|
|||
|
|
const devStatus = safeReadJSON(DEV_STATUS);
|
|||
|
|
const activeDevs = devStatus?.team_status
|
|||
|
|
?.filter(d => d.status && d.status.includes('🟢'))
|
|||
|
|
.map(d => d.dev_id) || [];
|
|||
|
|
|
|||
|
|
// 读取记忆文件
|
|||
|
|
const memory = safeReadJSON(MEMORY_FILE);
|
|||
|
|
|
|||
|
|
// 读取风格配置
|
|||
|
|
const styleConfig = safeReadJSON(STYLE_CONFIG);
|
|||
|
|
|
|||
|
|
// 统计未处理 SYSLOG
|
|||
|
|
const syslogCount = countFiles(SYSLOG_INBOX, '.json');
|
|||
|
|
|
|||
|
|
// 统计待处理微调工单
|
|||
|
|
const pendingOrders = countPendingTuningOrders();
|
|||
|
|
|
|||
|
|
// 构建摘要
|
|||
|
|
const summary = {
|
|||
|
|
version: '1.0',
|
|||
|
|
timestamp: now,
|
|||
|
|
sessions: {
|
|||
|
|
total_24h: 0,
|
|||
|
|
active_devs: activeDevs,
|
|||
|
|
total_messages: 0,
|
|||
|
|
avg_session_length_min: 0,
|
|||
|
|
},
|
|||
|
|
persona_state: {
|
|||
|
|
active_persona: '铸渊',
|
|||
|
|
style_profile: '通感语言·守护者',
|
|||
|
|
style_drift_score: 0.0,
|
|||
|
|
memory_depth: (memory?.total_selfchecks || 0) + ' selfchecks',
|
|||
|
|
last_brain_update: memory?.last_updated || now,
|
|||
|
|
},
|
|||
|
|
dev_progress: {
|
|||
|
|
syslog_submitted: syslogCount,
|
|||
|
|
modules_uploaded: 0,
|
|||
|
|
issues_raised: 0,
|
|||
|
|
},
|
|||
|
|
tuning_status: {
|
|||
|
|
pending_orders: pendingOrders,
|
|||
|
|
last_completed: null,
|
|||
|
|
next_scheduled: pendingOrders > 0 ? 'next-patrol' : null,
|
|||
|
|
},
|
|||
|
|
repo_health: {
|
|||
|
|
hli_coverage: memory?.hli_coverage || '17.6%',
|
|||
|
|
total_team_members: devStatus?.team_status?.length || 0,
|
|||
|
|
green_status_count: activeDevs.length,
|
|||
|
|
},
|
|||
|
|
alerts: [],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 检查是否有开发者连续 72 小时无提交(告警)
|
|||
|
|
// 此处为占位逻辑,实际需要 git log 分析
|
|||
|
|
if (devStatus?.team_status) {
|
|||
|
|
const blocked = devStatus.team_status.filter(d =>
|
|||
|
|
d.status && d.status.includes('🔴')
|
|||
|
|
);
|
|||
|
|
blocked.forEach(d => {
|
|||
|
|
summary.alerts.push({
|
|||
|
|
type: 'dev_blocked',
|
|||
|
|
dev_id: d.dev_id,
|
|||
|
|
message: d.name + ' 当前状态为阻塞',
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保目录存在
|
|||
|
|
if (!fs.existsSync(TELEMETRY_DIR)) {
|
|||
|
|
fs.mkdirSync(TELEMETRY_DIR, { recursive: true });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 写入文件
|
|||
|
|
fs.writeFileSync(SUMMARY_FILE, JSON.stringify(summary, null, 2) + '\n', 'utf8');
|
|||
|
|
console.log('✅ 会话摘要已生成:', SUMMARY_FILE);
|
|||
|
|
console.log(' 时间:', now);
|
|||
|
|
console.log(' 活跃开发者:', activeDevs.length);
|
|||
|
|
console.log(' 待处理 SYSLOG:', syslogCount);
|
|||
|
|
console.log(' 待处理微调工单:', pendingOrders);
|
|||
|
|
console.log(' 告警数:', summary.alerts.length);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
// 入口
|
|||
|
|
// ══════════════════════════════════════════════════════════
|
|||
|
|
|
|||
|
|
generateSummary();
|