guanghulab/scripts/agents/readme-generator.js

301 lines
8.5 KiB
JavaScript
Raw Permalink Normal View History

// scripts/agents/readme-generator.js
// README Generator · 首页动态布局引擎
// ZY-P1-README-002 · Phase 1 · README Management Agent
// 版权:国作登字-2026-A-00037559
'use strict';
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '../..');
// ── 皮肤系统 · 情绪驱动界面 ─────────────────────────────────────────────
const SKINS = {
CALM: { name: '🌊 湖面平静', emoji: ['🌊', '✅', '💎'], badgeColor: 'blue' },
BUILDING: { name: '⚡ 建设中', emoji: ['⚡', '🔨', '🚧'], badgeColor: 'yellow' },
ATTENTION: { name: '🔥 需要关注', emoji: ['🔥', '⚠️', '🔧'], badgeColor: 'orange' },
EMERGENCY: { name: '🚨 紧急状态', emoji: ['🚨', '❌', '🛑'], badgeColor: 'red' },
CELEBRATION: { name: '🎉 庆祝模式', emoji: ['🎉', '🏆', '✨'], badgeColor: 'brightgreen' }
};
/**
* 根据系统状态选择皮肤
* @param {object} systemState
* @returns {object} skin
*/
function selectSkin(systemState) {
if (!systemState) return SKINS.CALM;
// 紧急状态:有高级别告警
if (systemState.alert && systemState.alert.level === 'high') {
return SKINS.EMERGENCY;
}
// 需要关注:有中级别告警
if (systemState.alert && systemState.alert.level === 'medium') {
return SKINS.ATTENTION;
}
// 庆祝模式:平衡度完美且有里程碑
if (systemState.balanced && systemState.milestone) {
return SKINS.CELEBRATION;
}
// 建设中:有活跃开发
if (systemState.building || (systemState.recentUpdates && systemState.recentUpdates.length > 0)) {
return SKINS.BUILDING;
}
return SKINS.CALM;
}
/**
* 生成布局排序 · 按优先级排列版块
* @param {object} systemState
* @returns {string[]} 排序后的版块名称列表
*/
function generateLayout(systemState) {
const sections = [];
// P0: 紧急状态优先
if (systemState && systemState.alert && systemState.alert.level === 'high') {
sections.push('critical_alert');
}
// P1: 平衡偏差
if (systemState && !systemState.balanced) {
sections.push('balance_drift');
}
// P2: 最近合并
if (systemState && systemState.recentUpdates && systemState.recentUpdates.length > 0) {
sections.push('recent_updates');
}
// P3: 常规内容
sections.push('dashboard', 'twin_balance', 'bulletin');
return sections;
}
/**
* 渲染系统仪表板
* @param {object} state
* @returns {string} markdown
*/
function renderDashboard(state) {
const skin = selectSkin(state);
const now = new Date().toISOString().split('T')[0];
let md = `## ${skin.emoji[0]} 系统状态\n\n`;
md += `| 指标 | 状态 |\n`;
md += `|------|------|\n`;
md += `| 皮肤 | ${skin.name} |\n`;
md += `| 更新时间 | ${now} |\n`;
if (state && state.balance !== undefined) {
md += `| 平衡度 | ${(state.balance * 100).toFixed(1)}% |\n`;
}
if (state && state.alert) {
md += `| 告警 | ${state.alert.level} - ${state.alert.laggingSide} |\n`;
}
return md;
}
/**
* 渲染双子天平可视化
* @param {object} state
* @returns {string} markdown
*/
function renderTwinBalance(state) {
let md = '## ⚖️ 双子天平\n\n';
if (!state || !state.metrics) {
md += '_暂无天平数据_\n';
return md;
}
const left = state.metrics.left || {};
const right = state.metrics.right || {};
md += '| 维度 | EXE-Engine | Grid-DB |\n';
md += '|------|-----------|--------|\n';
const dims = ['taskCompletion', 'testPassRate', 'codeCoverage', 'securityScore'];
const dimNames = { taskCompletion: '任务完成', testPassRate: '测试通过', codeCoverage: '代码覆盖', securityScore: '安全扫描' };
for (const dim of dims) {
const lv = left.dimensions ? left.dimensions[dim] : 0;
const rv = right.dimensions ? right.dimensions[dim] : 0;
const lBar = generateProgressBar(lv);
const rBar = generateProgressBar(rv);
md += `| ${dimNames[dim]} | ${lBar} ${(lv * 100).toFixed(0)}% | ${rBar} ${(rv * 100).toFixed(0)}% |\n`;
}
md += `\n**综合**: EXE=${left.composite || 0} · GDB=${right.composite || 0} · 平衡度=${state.balance || 0}\n`;
return md;
}
/**
* 生成文本进度条
* @param {number} value - 0~1
* @returns {string}
*/
function generateProgressBar(value) {
const filled = Math.round((value || 0) * 10);
const empty = 10 - filled;
return '█'.repeat(filled) + '░'.repeat(empty);
}
/**
* 渲染公告板
* @param {object[]} events
* @returns {string} markdown
*/
function renderBulletin(events) {
let md = '## 📋 公告板\n\n';
if (!events || events.length === 0) {
md += '_暂无公告_\n';
return md;
}
md += '| 时间 | 事件 | 状态 | 处理 |\n';
md += '|------|------|------|------|\n';
for (const evt of events.slice(0, 10)) {
const time = evt.timestamp ? evt.timestamp.split('T')[0] : '-';
md += `| ${time} | ${evt.event || '-'} | ${evt.status || '-'} | ${evt.handler || '-'} |\n`;
}
return md;
}
/**
* 渲染最近更新
* @param {object[]} updates
* @returns {string} markdown
*/
function renderRecentUpdates(updates) {
let md = '## 📝 最近更新\n\n';
if (!updates || updates.length === 0) {
md += '_暂无更新_\n';
return md;
}
for (const u of updates.slice(0, 5)) {
md += `- ${u.emoji || '📌'} ${u.description || u}\n`;
}
return md;
}
/**
* 生成完整 README
* @param {object} systemState
* @returns {string} markdown
*/
function generateReadme(systemState) {
const state = systemState || {};
const skin = selectSkin(state);
const layout = generateLayout(state);
let md = `# ${skin.emoji[0]} 光湖 · GuangHuLab\n\n`;
md += `> 数字地球本体论 · Digital Earth Ontology\n`;
md += `> 版权:国作登字-2026-A-00037559\n\n`;
for (const section of layout) {
switch (section) {
case 'critical_alert':
md += `## 🚨 紧急告警\n\n`;
if (state.alert) {
md += `**${state.alert.level}**: ${state.alert.laggingSide} 落后 · 偏差 ${state.alert.deficit}\n\n`;
md += `> ${state.alert.recommendation}\n\n`;
}
break;
case 'balance_drift':
md += `## ⚠️ 平衡偏差\n\n`;
md += `偏差值: ${state.drift || 0} · 落后侧: ${state.alert ? state.alert.laggingSide : '未知'}\n\n`;
break;
case 'dashboard':
md += renderDashboard(state);
md += '\n';
break;
case 'twin_balance':
md += renderTwinBalance(state);
md += '\n';
break;
case 'bulletin':
md += renderBulletin(state.events || []);
md += '\n';
break;
case 'recent_updates':
md += renderRecentUpdates(state.recentUpdates || []);
md += '\n';
break;
}
}
md += '---\n';
md += `_由 铸渊 (AG-ZY-01) 自动生成 · ${new Date().toISOString().split('T')[0]}_\n`;
return md;
}
// ── CLI 入口 ─────────────────────────────────────────────────────────────
if (require.main === module) {
const args = process.argv.slice(2);
if (args.includes('--auto')) {
// 自动模式:读取天眼数据生成 README
console.log('📡 README Generator · 自动更新模式\n');
let state = {};
const statusPath = path.join(ROOT, '.github/tianyen/twin-status.json');
const bulletinPath = path.join(ROOT, '.github/tianyen/bulletin-data.json');
if (fs.existsSync(statusPath)) {
try {
state = JSON.parse(fs.readFileSync(statusPath, 'utf8'));
} catch {
// twin-status.json 解析失败,使用空状态生成 README
}
}
if (fs.existsSync(bulletinPath)) {
try {
const bulletin = JSON.parse(fs.readFileSync(bulletinPath, 'utf8'));
state.events = bulletin.events || [];
} catch {
// bulletin-data.json 解析失败,跳过公告数据
}
}
state.building = true;
const readme = generateReadme(state);
fs.writeFileSync(path.join(ROOT, 'README.md'), readme, 'utf8');
console.log('✅ README.md 已更新');
} else {
console.log('📡 README Generator · 预览模式\n');
const preview = generateReadme({ building: true });
console.log(preview);
}
}
module.exports = {
SKINS,
selectSkin,
generateLayout,
renderDashboard,
renderTwinBalance,
renderBulletin,
renderRecentUpdates,
generateReadme,
generateProgressBar
};