feat: add weekly swarm status report generation (#11)

This commit is contained in:
2026-03-04 18:16:52 -08:00
parent e20ebdf871
commit bc53380d4a

View File

@@ -229,3 +229,142 @@ function setupGiteaRoutes(app) {
} }
module.exports = { setupGiteaRoutes }; module.exports = { setupGiteaRoutes };
// Weekly swarm status report
app.get('/api/swarm/weekly-report', async (req, res) => {
try {
const repos = await gitea.getRepos();
const tasks = await fetch(`${process.env.TASKBOARD_URL || 'http://localhost:8395'}/api/tasks`).then(r => r.json());
const agents = await fetch(`${process.env.TASKBOARD_URL || 'http://localhost:8395'}/api/agents`).then(r => r.json());
// Get usage data
const usageRes = await fetch(`${process.env.TASKBOARD_URL || 'http://localhost:8395'}/api/usage/real`);
const usage = await usageRes.json();
// Generate report
const report = {
generated: new Date().toISOString(),
weekOf: new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),
repositories: {
total: repos.length,
healthy: repos.filter(r => {
const days = Math.floor((Date.now() - new Date(r.updated_at).getTime()) / 86400000);
return days <= 7;
}).length,
stale: repos.filter(r => {
const days = Math.floor((Date.now() - new Date(r.updated_at).getTime()) / 86400000);
return days > 30;
}).length
},
tasks: {
total: tasks.length,
done: tasks.filter(t => t.status === 'Done').length,
inProgress: tasks.filter(t => t.status === 'In Progress').length,
backlog: tasks.filter(t => t.status === 'Backlog').length,
byPriority: {
Critical: tasks.filter(t => t.priority === 'Critical').length,
High: tasks.filter(t => t.priority === 'High').length,
Medium: tasks.filter(t => t.priority === 'Medium').length,
Low: tasks.filter(t => t.priority === 'Low').length
}
},
agents: {
total: agents.length,
byStatus: {
active: agents.filter(a => a.status === 'active').length,
idle: agents.filter(a => a.status === 'idle').length,
offline: agents.filter(a => a.status === 'offline').length
}
},
usage: {
totalTokens: usage.totals?.total || 0,
totalCost: usage.totals?.cost || 0,
topAgents: Object.entries(usage.agents || {})
.map(([k, v]) => ({ agent: k, tokens: v.total }))
.sort((a, b) => b.tokens - a.tokens)
.slice(0, 5)
},
summary: `Swarm weekly report for ${new Date().toLocaleDateString('en-US', { weekday: 'long' })}.\n\n📊 **Repositories:** ${repos.length} total (${repos.filter(r => r.updated_at && new Date(r.updated_at) > new Date(Date.now() - 7 * 86400000)).length} active this week)\n\n📋 **Tasks:** ${tasks.length} total (${tasks.filter(t => t.status === 'Done').length} done, ${tasks.filter(t => t.status === 'In Progress').length} in progress)\n\n🤖 **Agents:** ${agents.length} total\n\n💰 **Usage:** ${new Intl.NumberFormat('en-US').format(usage.totals?.total || 0)} tokens this week`
};
// Generate markdown
const markdown = generateWeeklyReportMarkdown(report);
// Save to wiki
const wikiPath = path.join(process.env.WIKI_DIR || '/app/wiki', `weekly-report-${new Date().toISOString().slice(0, 10)}.md`);
fs.writeFileSync(wikiPath, JSON.stringify({
title: `Weekly Report - ${new Date().toLocaleDateString()}`,
content: markdown,
tags: ['report', 'weekly', 'swarm', 'auto-generated'],
created: new Date().toISOString(),
modified: new Date().toISOString()
}, 'utf8');
res.json({
success: true,
report,
wikiEntry: {
title: `Weekly Report - ${new Date().toLocaleDateString()}`,
filename: path.basename(wikiPath)
}
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
function generateWeeklyReportMarkdown(report) {
return `# Weekly Swarm Status Report
**Generated:** ${new Date(report.generated).toLocaleString()}
## 📊 Repositories
| Metric | Count |
|--------|-------|
| Total | ${report.repositories.total} |
| Healthy (≤7d) | ${report.repositories.healthy} |
| Stale (>30d) | ${report.repositories.stale} |
## 📋 Tasks
| Priority | Count |
|----------|-------|
| Critical | ${tasks.byPriority.Critical} |
| High | ${tasks.byPriority.High} |
| Medium | ${tasks.byPriority.Medium} |
| Low | ${tasks.byPriority.Low} |
**Summary:**
- Total: ${report.tasks.total}
- Done: ${report.tasks.done}
- In Progress: ${report.tasks.inProgress}
- Backlog: ${report.tasks.backlog}
## 🤖 Agents
**Total Agents:** ${report.agents.total}
| Status | Count |
|--------|-------|
| Active | ${report.agents.byStatus.active} |
| Idle | ${report.agents.byStatus.idle} |
| Offline | ${report.agents.byStatus.offline} |
## 💰 Usage
**Total Tokens:** ${new Intl.NumberFormat('en-US').format(report.usage.totalTokens)}
**Total Cost:** $${report.usage.totalCost.toFixed(2)}
### Top 5 Agents by Usage
| Rank | Agent | Tokens |
|------|-------|--------|
${report.usage.topAgents.map((a, i) => `| ${i + 1} | \`${a.agent}\` | ${new Intl.NumberFormat('en-US').format(a.tokens)} |`).join('\n')}
---
*Report auto-generated by OpenClaw Swarm Orchestrator*
`;
}