@harbinger-ai/harbinger 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +406 -0
- package/agents/README.md +76 -0
- package/agents/_template/CONFIG.yaml +7 -0
- package/agents/_template/HEARTBEAT.md +59 -0
- package/agents/_template/IDENTITY.md +4 -0
- package/agents/_template/SKILLS.md +1 -0
- package/agents/_template/SOUL.md +25 -0
- package/agents/_template/TOOLS.md +3 -0
- package/agents/binary-reverser/CONFIG.yaml +21 -0
- package/agents/binary-reverser/HEARTBEAT.md +65 -0
- package/agents/binary-reverser/IDENTITY.md +1 -0
- package/agents/binary-reverser/SKILLS.md +1 -0
- package/agents/binary-reverser/SOUL.md +23 -0
- package/agents/binary-reverser/TOOLS.md +99 -0
- package/agents/browser-agent/CONFIG.yaml +20 -0
- package/agents/browser-agent/HEARTBEAT.md +79 -0
- package/agents/browser-agent/IDENTITY.md +5 -0
- package/agents/browser-agent/SKILLS.md +86 -0
- package/agents/browser-agent/SOUL.md +23 -0
- package/agents/browser-agent/TOOLS.md +186 -0
- package/agents/cloud-infiltrator/CONFIG.yaml +22 -0
- package/agents/cloud-infiltrator/HEARTBEAT.md +78 -0
- package/agents/cloud-infiltrator/IDENTITY.md +1 -0
- package/agents/cloud-infiltrator/SKILLS.md +1 -0
- package/agents/cloud-infiltrator/SOUL.md +23 -0
- package/agents/cloud-infiltrator/TOOLS.md +68 -0
- package/agents/coding-assistant/CONFIG.yaml +22 -0
- package/agents/coding-assistant/HEARTBEAT.md +57 -0
- package/agents/coding-assistant/IDENTITY.md +5 -0
- package/agents/coding-assistant/SKILLS.md +69 -0
- package/agents/coding-assistant/SOUL.md +60 -0
- package/agents/coding-assistant/TOOLS.md +168 -0
- package/agents/learning-agent/CONFIG.yaml +21 -0
- package/agents/learning-agent/HEARTBEAT.md +63 -0
- package/agents/learning-agent/IDENTITY.md +5 -0
- package/agents/learning-agent/SKILLS.md +86 -0
- package/agents/learning-agent/SOUL.md +77 -0
- package/agents/learning-agent/TOOLS.md +145 -0
- package/agents/maintainer/CONFIG.yaml +31 -0
- package/agents/maintainer/HEARTBEAT.md +28 -0
- package/agents/maintainer/IDENTITY.md +33 -0
- package/agents/maintainer/SKILLS.md +24 -0
- package/agents/maintainer/SOUL.md +61 -0
- package/agents/maintainer/TOOLS.md +29 -0
- package/agents/maintainer/lib/engine.js +279 -0
- package/agents/maintainer/lib/safe-fixer.js +183 -0
- package/agents/morning-brief/CONFIG.yaml +22 -0
- package/agents/morning-brief/HEARTBEAT.md +60 -0
- package/agents/morning-brief/IDENTITY.md +5 -0
- package/agents/morning-brief/SKILLS.md +56 -0
- package/agents/morning-brief/SOUL.md +64 -0
- package/agents/morning-brief/TOOLS.md +112 -0
- package/agents/osint-detective/CONFIG.yaml +24 -0
- package/agents/osint-detective/HEARTBEAT.md +66 -0
- package/agents/osint-detective/IDENTITY.md +1 -0
- package/agents/osint-detective/SKILLS.md +1 -0
- package/agents/osint-detective/SOUL.md +23 -0
- package/agents/osint-detective/TOOLS.md +81 -0
- package/agents/recon-scout/CONFIG.yaml +22 -0
- package/agents/recon-scout/HEARTBEAT.md +79 -0
- package/agents/recon-scout/IDENTITY.md +1 -0
- package/agents/recon-scout/SKILLS.md +1 -0
- package/agents/recon-scout/SOUL.md +23 -0
- package/agents/recon-scout/TOOLS.md +93 -0
- package/agents/report-writer/CONFIG.yaml +21 -0
- package/agents/report-writer/HEARTBEAT.md +63 -0
- package/agents/report-writer/IDENTITY.md +1 -0
- package/agents/report-writer/SKILLS.md +1 -0
- package/agents/report-writer/SOUL.md +23 -0
- package/agents/report-writer/TOOLS.md +69 -0
- package/agents/shared/README.md +13 -0
- package/agents/web-hacker/CONFIG.yaml +24 -0
- package/agents/web-hacker/HEARTBEAT.md +78 -0
- package/agents/web-hacker/IDENTITY.md +1 -0
- package/agents/web-hacker/SKILLS.md +1 -0
- package/agents/web-hacker/SOUL.md +23 -0
- package/agents/web-hacker/TOOLS.md +86 -0
- package/api/CLAUDE.md +19 -0
- package/api/index.js +274 -0
- package/bin/cli.js +620 -0
- package/bin/local.sh +31 -0
- package/bin/postinstall.js +63 -0
- package/config/index.js +24 -0
- package/config/instrumentation.js +93 -0
- package/drizzle/0000_initial.sql +52 -0
- package/drizzle/0001_bounty_and_registry.sql +82 -0
- package/drizzle/0002_sync_columns.sql +7 -0
- package/drizzle/0003_graceful_bloodscream.sql +86 -0
- package/drizzle/meta/0000_snapshot.json +321 -0
- package/drizzle/meta/0003_snapshot.json +878 -0
- package/drizzle/meta/_journal.json +34 -0
- package/drizzle/relations.ts +3 -0
- package/drizzle/schema.ts +145 -0
- package/lib/actions.js +47 -0
- package/lib/agents.js +166 -0
- package/lib/ai/agent.js +96 -0
- package/lib/ai/autonomous-engine.js +261 -0
- package/lib/ai/index.js +359 -0
- package/lib/ai/model-router.js +254 -0
- package/lib/ai/model.js +73 -0
- package/lib/ai/tools.js +84 -0
- package/lib/auth/actions.js +28 -0
- package/lib/auth/config.js +27 -0
- package/lib/auth/edge-config.js +27 -0
- package/lib/auth/index.js +27 -0
- package/lib/auth/middleware.js +53 -0
- package/lib/bounty/actions.js +119 -0
- package/lib/bounty/findings.js +64 -0
- package/lib/bounty/programs.js +34 -0
- package/lib/bounty/sync-targets.js +267 -0
- package/lib/bounty/targets.js +33 -0
- package/lib/channels/base.js +56 -0
- package/lib/channels/index.js +15 -0
- package/lib/channels/telegram.js +148 -0
- package/lib/chat/actions.js +288 -0
- package/lib/chat/api.js +135 -0
- package/lib/chat/components/app-sidebar.js +237 -0
- package/lib/chat/components/app-sidebar.jsx +289 -0
- package/lib/chat/components/chat-header.js +27 -0
- package/lib/chat/components/chat-header.jsx +37 -0
- package/lib/chat/components/chat-input.js +230 -0
- package/lib/chat/components/chat-input.jsx +228 -0
- package/lib/chat/components/chat-nav-context.js +11 -0
- package/lib/chat/components/chat-nav-context.jsx +11 -0
- package/lib/chat/components/chat-page.js +81 -0
- package/lib/chat/components/chat-page.jsx +100 -0
- package/lib/chat/components/chat.js +150 -0
- package/lib/chat/components/chat.jsx +182 -0
- package/lib/chat/components/chats-page.js +302 -0
- package/lib/chat/components/chats-page.jsx +330 -0
- package/lib/chat/components/crons-page.js +172 -0
- package/lib/chat/components/crons-page.jsx +244 -0
- package/lib/chat/components/enhanced-tool-call.js +103 -0
- package/lib/chat/components/enhanced-tool-call.jsx +139 -0
- package/lib/chat/components/findings-page.js +175 -0
- package/lib/chat/components/findings-page.jsx +214 -0
- package/lib/chat/components/greeting.js +22 -0
- package/lib/chat/components/greeting.jsx +26 -0
- package/lib/chat/components/icons.js +777 -0
- package/lib/chat/components/icons.jsx +741 -0
- package/lib/chat/components/index.js +26 -0
- package/lib/chat/components/mcp-page.js +260 -0
- package/lib/chat/components/mcp-page.jsx +355 -0
- package/lib/chat/components/message.js +289 -0
- package/lib/chat/components/message.jsx +315 -0
- package/lib/chat/components/messages.js +66 -0
- package/lib/chat/components/messages.jsx +77 -0
- package/lib/chat/components/notifications-page.js +56 -0
- package/lib/chat/components/notifications-page.jsx +87 -0
- package/lib/chat/components/page-layout.js +21 -0
- package/lib/chat/components/page-layout.jsx +28 -0
- package/lib/chat/components/registry-page.js +222 -0
- package/lib/chat/components/registry-page.jsx +255 -0
- package/lib/chat/components/settings-layout.js +40 -0
- package/lib/chat/components/settings-layout.jsx +54 -0
- package/lib/chat/components/settings-secrets-page.js +216 -0
- package/lib/chat/components/settings-secrets-page.jsx +264 -0
- package/lib/chat/components/sidebar-history-item.js +132 -0
- package/lib/chat/components/sidebar-history-item.jsx +113 -0
- package/lib/chat/components/sidebar-history.js +115 -0
- package/lib/chat/components/sidebar-history.jsx +157 -0
- package/lib/chat/components/sidebar-user-nav.js +63 -0
- package/lib/chat/components/sidebar-user-nav.jsx +73 -0
- package/lib/chat/components/status-bar.js +39 -0
- package/lib/chat/components/status-bar.jsx +51 -0
- package/lib/chat/components/swarm-page.js +157 -0
- package/lib/chat/components/swarm-page.jsx +210 -0
- package/lib/chat/components/targets-page.js +376 -0
- package/lib/chat/components/targets-page.jsx +389 -0
- package/lib/chat/components/tool-call.js +86 -0
- package/lib/chat/components/tool-call.jsx +104 -0
- package/lib/chat/components/tool-panel.js +107 -0
- package/lib/chat/components/tool-panel.jsx +145 -0
- package/lib/chat/components/triggers-page.js +153 -0
- package/lib/chat/components/triggers-page.jsx +221 -0
- package/lib/chat/components/ui/confirm-dialog.js +53 -0
- package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
- package/lib/chat/components/ui/dropdown-menu.js +98 -0
- package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
- package/lib/chat/components/ui/rename-dialog.js +74 -0
- package/lib/chat/components/ui/rename-dialog.jsx +72 -0
- package/lib/chat/components/ui/scroll-area.js +13 -0
- package/lib/chat/components/ui/scroll-area.jsx +17 -0
- package/lib/chat/components/ui/separator.js +21 -0
- package/lib/chat/components/ui/separator.jsx +18 -0
- package/lib/chat/components/ui/sheet.js +75 -0
- package/lib/chat/components/ui/sheet.jsx +95 -0
- package/lib/chat/components/ui/sidebar.js +227 -0
- package/lib/chat/components/ui/sidebar.jsx +245 -0
- package/lib/chat/components/ui/tooltip.js +56 -0
- package/lib/chat/components/ui/tooltip.jsx +66 -0
- package/lib/chat/components/upgrade-dialog.js +151 -0
- package/lib/chat/components/upgrade-dialog.jsx +170 -0
- package/lib/chat/utils.js +11 -0
- package/lib/cron.js +246 -0
- package/lib/db/api-keys.js +163 -0
- package/lib/db/chats.js +145 -0
- package/lib/db/index.js +52 -0
- package/lib/db/notifications.js +99 -0
- package/lib/db/schema.js +145 -0
- package/lib/db/update-check.js +96 -0
- package/lib/db/users.js +89 -0
- package/lib/mcp/actions.js +104 -0
- package/lib/mcp/client.js +79 -0
- package/lib/mcp/handler.js +57 -0
- package/lib/mcp/server.js +165 -0
- package/lib/paths.js +46 -0
- package/lib/registry/actions.js +164 -0
- package/lib/registry/catalog.js +137 -0
- package/lib/registry/tools.js +71 -0
- package/lib/tools/create-job.js +99 -0
- package/lib/tools/github.js +217 -0
- package/lib/tools/openai.js +35 -0
- package/lib/tools/telegram.js +292 -0
- package/lib/triggers.js +118 -0
- package/lib/utils/render-md.js +102 -0
- package/package.json +103 -0
- package/setup/lib/auth.mjs +81 -0
- package/setup/lib/env.mjs +21 -0
- package/setup/lib/fs-utils.mjs +20 -0
- package/setup/lib/github.mjs +149 -0
- package/setup/lib/prerequisites.mjs +155 -0
- package/setup/lib/prompts.mjs +267 -0
- package/setup/lib/providers.mjs +48 -0
- package/setup/lib/sync.mjs +125 -0
- package/setup/lib/targets.mjs +45 -0
- package/setup/lib/telegram-verify.mjs +63 -0
- package/setup/lib/telegram.mjs +76 -0
- package/setup/setup-telegram.mjs +264 -0
- package/setup/setup.mjs +842 -0
- package/templates/.dockerignore +5 -0
- package/templates/.env.example +63 -0
- package/templates/.github/workflows/auto-merge.yml +117 -0
- package/templates/.github/workflows/build-image.yml +36 -0
- package/templates/.github/workflows/notify-job-failed.yml +64 -0
- package/templates/.github/workflows/notify-pr-complete.yml +119 -0
- package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
- package/templates/.github/workflows/run-job.yml +89 -0
- package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
- package/templates/.gitignore.template +45 -0
- package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
- package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
- package/templates/CLAUDE.md +29 -0
- package/templates/CLAUDE.md.template +307 -0
- package/templates/app/api/[...thepopebot]/route.js +1 -0
- package/templates/app/api/auth/[...nextauth]/route.js +1 -0
- package/templates/app/chat/[chatId]/page.js +8 -0
- package/templates/app/chats/page.js +7 -0
- package/templates/app/components/ascii-logo.jsx +10 -0
- package/templates/app/components/login-form.jsx +92 -0
- package/templates/app/components/setup-form.jsx +82 -0
- package/templates/app/components/theme-provider.jsx +11 -0
- package/templates/app/components/theme-toggle.jsx +38 -0
- package/templates/app/components/ui/button.jsx +21 -0
- package/templates/app/components/ui/card.jsx +23 -0
- package/templates/app/components/ui/input.jsx +10 -0
- package/templates/app/components/ui/label.jsx +10 -0
- package/templates/app/crons/page.js +5 -0
- package/templates/app/findings/page.js +7 -0
- package/templates/app/globals.css +90 -0
- package/templates/app/layout.js +19 -0
- package/templates/app/login/page.js +15 -0
- package/templates/app/notifications/page.js +7 -0
- package/templates/app/page.js +7 -0
- package/templates/app/settings/crons/page.js +5 -0
- package/templates/app/settings/layout.js +7 -0
- package/templates/app/settings/mcp/page.js +5 -0
- package/templates/app/settings/page.js +5 -0
- package/templates/app/settings/secrets/page.js +5 -0
- package/templates/app/settings/triggers/page.js +5 -0
- package/templates/app/stream/chat/route.js +1 -0
- package/templates/app/swarm/page.js +7 -0
- package/templates/app/targets/page.js +7 -0
- package/templates/app/toolbox/page.js +7 -0
- package/templates/app/triggers/page.js +5 -0
- package/templates/config/AGENT.md +34 -0
- package/templates/config/CRONS.json +56 -0
- package/templates/config/EVENT_HANDLER.md +224 -0
- package/templates/config/HEARTBEAT.md +3 -0
- package/templates/config/JOB_SUMMARY.md +130 -0
- package/templates/config/MCP_SERVERS.json +1 -0
- package/templates/config/SKILL_BUILDING_GUIDE.md +90 -0
- package/templates/config/SOUL.md +17 -0
- package/templates/config/TRIGGERS.json +58 -0
- package/templates/docker/event-handler/Dockerfile +20 -0
- package/templates/docker/event-handler/ecosystem.config.cjs +8 -0
- package/templates/docker/job-claude-code/Dockerfile +34 -0
- package/templates/docker/job-claude-code/entrypoint.sh +139 -0
- package/templates/docker/job-pi-coding-agent/Dockerfile +44 -0
- package/templates/docker/job-pi-coding-agent/entrypoint.sh +163 -0
- package/templates/docker-compose.yml +63 -0
- package/templates/instrumentation.js +6 -0
- package/templates/middleware.js +1 -0
- package/templates/next.config.mjs +3 -0
- package/templates/postcss.config.mjs +5 -0
- package/templates/skills/LICENSE +21 -0
- package/templates/skills/README.md +119 -0
- package/templates/skills/brave-search/SKILL.md +79 -0
- package/templates/skills/brave-search/content.js +86 -0
- package/templates/skills/brave-search/package-lock.json +621 -0
- package/templates/skills/brave-search/package.json +14 -0
- package/templates/skills/brave-search/search.js +199 -0
- package/templates/skills/browser-tools/SKILL.md +196 -0
- package/templates/skills/browser-tools/browser-content.js +103 -0
- package/templates/skills/browser-tools/browser-cookies.js +35 -0
- package/templates/skills/browser-tools/browser-eval.js +53 -0
- package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
- package/templates/skills/browser-tools/browser-nav.js +44 -0
- package/templates/skills/browser-tools/browser-pick.js +162 -0
- package/templates/skills/browser-tools/browser-screenshot.js +34 -0
- package/templates/skills/browser-tools/browser-start.js +87 -0
- package/templates/skills/browser-tools/package-lock.json +2556 -0
- package/templates/skills/browser-tools/package.json +19 -0
- package/templates/skills/llm-secrets/SKILL.md +34 -0
- package/templates/skills/llm-secrets/llm-secrets.js +33 -0
- package/templates/skills/modify-self/SKILL.md +12 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MaintainerEngine — Orchestrates nightly code quality cycles.
|
|
3
|
+
*
|
|
4
|
+
* Workflow: scan → identify → fix → test → report → PR → notify
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process')
|
|
8
|
+
const path = require('path')
|
|
9
|
+
|
|
10
|
+
class MaintainerEngine {
|
|
11
|
+
constructor(opts = {}) {
|
|
12
|
+
this.projectRoot = opts.projectRoot || process.cwd()
|
|
13
|
+
this.apiBase = opts.apiBase || process.env.THEPOPEBOT_API || 'http://localhost:8080'
|
|
14
|
+
this.dryRun = opts.dryRun || false
|
|
15
|
+
this.channels = opts.channels || [] // ['discord', 'telegram']
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Full nightly maintenance cycle.
|
|
20
|
+
* Returns a structured report object.
|
|
21
|
+
*/
|
|
22
|
+
async runNightly() {
|
|
23
|
+
const report = {
|
|
24
|
+
startedAt: new Date().toISOString(),
|
|
25
|
+
scans: {},
|
|
26
|
+
fixes: [],
|
|
27
|
+
buildPass: false,
|
|
28
|
+
score: 0,
|
|
29
|
+
prUrl: null,
|
|
30
|
+
errors: [],
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
// 1. Run health scans
|
|
35
|
+
report.scans = this.runScans()
|
|
36
|
+
|
|
37
|
+
// 2. Identify fixable issues
|
|
38
|
+
const { autoFixable, requiresApproval } = this.categorize(report.scans)
|
|
39
|
+
report.autoFixable = autoFixable.length
|
|
40
|
+
report.requiresApproval = requiresApproval.length
|
|
41
|
+
|
|
42
|
+
// 3. Apply safe fixes
|
|
43
|
+
if (!this.dryRun && autoFixable.length > 0) {
|
|
44
|
+
report.fixes = this.applySafeFixes(autoFixable)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 4. Verify build
|
|
48
|
+
report.buildPass = this.verifyBuild()
|
|
49
|
+
if (!report.buildPass && report.fixes.length > 0) {
|
|
50
|
+
this.rollbackFixes()
|
|
51
|
+
report.fixes = []
|
|
52
|
+
report.errors.push('Build failed after fixes — rolled back all changes')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 5. Compute health score
|
|
56
|
+
report.score = this.computeScore(report.scans)
|
|
57
|
+
|
|
58
|
+
// 6. Store metrics via API
|
|
59
|
+
await this.storeMetrics(report)
|
|
60
|
+
|
|
61
|
+
// 7. Create PR if there are committed fixes
|
|
62
|
+
if (report.fixes.length > 0 && !this.dryRun) {
|
|
63
|
+
report.prUrl = this.createPR(report)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 8. Notify channels
|
|
67
|
+
await this.notify(report)
|
|
68
|
+
|
|
69
|
+
} catch (err) {
|
|
70
|
+
report.errors.push(err.message)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
report.completedAt = new Date().toISOString()
|
|
74
|
+
return report
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Run all health scans. Returns structured metrics.
|
|
79
|
+
*/
|
|
80
|
+
runScans() {
|
|
81
|
+
const scans = {}
|
|
82
|
+
|
|
83
|
+
// Count `any` types in TypeScript
|
|
84
|
+
try {
|
|
85
|
+
const result = execSync(
|
|
86
|
+
`rg -c ': any\\b' --type ts --glob '!node_modules' --glob '!dist' ${this.projectRoot} 2>/dev/null || true`,
|
|
87
|
+
{ encoding: 'utf-8' }
|
|
88
|
+
)
|
|
89
|
+
scans.anyTypes = result.split('\n').filter(Boolean).reduce((sum, line) => {
|
|
90
|
+
const count = parseInt(line.split(':').pop(), 10)
|
|
91
|
+
return sum + (isNaN(count) ? 0 : count)
|
|
92
|
+
}, 0)
|
|
93
|
+
} catch { scans.anyTypes = 0 }
|
|
94
|
+
|
|
95
|
+
// Count console.log statements
|
|
96
|
+
try {
|
|
97
|
+
const result = execSync(
|
|
98
|
+
`rg -c 'console\\.log' --type ts --type tsx --glob '!node_modules' --glob '!dist' ${this.projectRoot} 2>/dev/null || true`,
|
|
99
|
+
{ encoding: 'utf-8' }
|
|
100
|
+
)
|
|
101
|
+
scans.consoleLogs = result.split('\n').filter(Boolean).reduce((sum, line) => {
|
|
102
|
+
const count = parseInt(line.split(':').pop(), 10)
|
|
103
|
+
return sum + (isNaN(count) ? 0 : count)
|
|
104
|
+
}, 0)
|
|
105
|
+
} catch { scans.consoleLogs = 0 }
|
|
106
|
+
|
|
107
|
+
// Count outdated dependencies
|
|
108
|
+
try {
|
|
109
|
+
const result = execSync('pnpm outdated --format json 2>/dev/null || echo "[]"', {
|
|
110
|
+
encoding: 'utf-8', cwd: this.projectRoot
|
|
111
|
+
})
|
|
112
|
+
const deps = JSON.parse(result || '[]')
|
|
113
|
+
scans.depsOutdated = Array.isArray(deps) ? deps.length : 0
|
|
114
|
+
} catch { scans.depsOutdated = 0 }
|
|
115
|
+
|
|
116
|
+
// Test coverage (placeholder — reads from coverage summary if exists)
|
|
117
|
+
scans.testCoverage = 0
|
|
118
|
+
try {
|
|
119
|
+
const coveragePath = path.join(this.projectRoot, 'coverage', 'coverage-summary.json')
|
|
120
|
+
const coverage = require(coveragePath)
|
|
121
|
+
scans.testCoverage = Math.round(coverage.total?.lines?.pct || 0)
|
|
122
|
+
} catch { /* no coverage data */ }
|
|
123
|
+
|
|
124
|
+
// Convention violations
|
|
125
|
+
scans.conventions = 0
|
|
126
|
+
try {
|
|
127
|
+
// Check for hardcoded colors outside design tokens
|
|
128
|
+
const result = execSync(
|
|
129
|
+
`rg -c '#[0-9a-fA-F]{6}' --type tsx --glob '!node_modules' --glob '!*.css' ${this.projectRoot} 2>/dev/null || true`,
|
|
130
|
+
{ encoding: 'utf-8' }
|
|
131
|
+
)
|
|
132
|
+
scans.conventions = result.split('\n').filter(Boolean).length
|
|
133
|
+
} catch { /* skip */ }
|
|
134
|
+
|
|
135
|
+
return scans
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Categorize issues into auto-fixable vs requires-approval.
|
|
140
|
+
*/
|
|
141
|
+
categorize(scans) {
|
|
142
|
+
const autoFixable = []
|
|
143
|
+
const requiresApproval = []
|
|
144
|
+
|
|
145
|
+
if (scans.consoleLogs > 0) {
|
|
146
|
+
autoFixable.push({ type: 'console-log', count: scans.consoleLogs })
|
|
147
|
+
}
|
|
148
|
+
if (scans.anyTypes > 0) {
|
|
149
|
+
requiresApproval.push({ type: 'any-types', count: scans.anyTypes })
|
|
150
|
+
}
|
|
151
|
+
if (scans.depsOutdated > 0) {
|
|
152
|
+
requiresApproval.push({ type: 'deps-outdated', count: scans.depsOutdated })
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { autoFixable, requiresApproval }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Apply safe fixes. Returns list of applied fixes.
|
|
160
|
+
*/
|
|
161
|
+
applySafeFixes(issues) {
|
|
162
|
+
const SafeFixer = require('./safe-fixer')
|
|
163
|
+
const fixer = new SafeFixer({ projectRoot: this.projectRoot })
|
|
164
|
+
return fixer.apply(issues)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Verify the build passes after fixes.
|
|
169
|
+
*/
|
|
170
|
+
verifyBuild() {
|
|
171
|
+
try {
|
|
172
|
+
execSync('cd backend && go build -o /dev/null ./cmd/', {
|
|
173
|
+
cwd: this.projectRoot, stdio: 'pipe', timeout: 60000
|
|
174
|
+
})
|
|
175
|
+
execSync('pnpm build:ui', {
|
|
176
|
+
cwd: this.projectRoot, stdio: 'pipe', timeout: 120000
|
|
177
|
+
})
|
|
178
|
+
return true
|
|
179
|
+
} catch {
|
|
180
|
+
return false
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Rollback all uncommitted changes.
|
|
186
|
+
*/
|
|
187
|
+
rollbackFixes() {
|
|
188
|
+
try {
|
|
189
|
+
execSync('git checkout -- .', { cwd: this.projectRoot, stdio: 'pipe' })
|
|
190
|
+
} catch { /* already clean */ }
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Compute health score (0-100).
|
|
195
|
+
*/
|
|
196
|
+
computeScore(scans) {
|
|
197
|
+
let score = 100
|
|
198
|
+
score -= (scans.anyTypes || 0) * 2
|
|
199
|
+
score -= (scans.consoleLogs || 0)
|
|
200
|
+
score -= (scans.depsOutdated || 0) * 3
|
|
201
|
+
score += (scans.testCoverage || 0) // bonus for coverage
|
|
202
|
+
return Math.max(0, Math.min(100, score))
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Store metrics to the Harbinger API.
|
|
207
|
+
*/
|
|
208
|
+
async storeMetrics(report) {
|
|
209
|
+
try {
|
|
210
|
+
const body = JSON.stringify({
|
|
211
|
+
date: new Date().toISOString().split('T')[0],
|
|
212
|
+
any_types: report.scans.anyTypes || 0,
|
|
213
|
+
console_logs: report.scans.consoleLogs || 0,
|
|
214
|
+
test_coverage: report.scans.testCoverage || 0,
|
|
215
|
+
deps_outdated: report.scans.depsOutdated || 0,
|
|
216
|
+
conventions: report.scans.conventions || 0,
|
|
217
|
+
score: report.score,
|
|
218
|
+
})
|
|
219
|
+
await fetch(`${this.apiBase}/api/health/code`, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: { 'Content-Type': 'application/json' },
|
|
222
|
+
body,
|
|
223
|
+
})
|
|
224
|
+
} catch (err) {
|
|
225
|
+
report.errors.push(`Failed to store metrics: ${err.message}`)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Create a GitHub PR with the maintenance report.
|
|
231
|
+
*/
|
|
232
|
+
createPR(report) {
|
|
233
|
+
const date = new Date().toISOString().split('T')[0]
|
|
234
|
+
const branch = `maintainer/${date}`
|
|
235
|
+
|
|
236
|
+
try {
|
|
237
|
+
execSync(`git checkout -b ${branch}`, { cwd: this.projectRoot, stdio: 'pipe' })
|
|
238
|
+
execSync('git add -A', { cwd: this.projectRoot, stdio: 'pipe' })
|
|
239
|
+
execSync(
|
|
240
|
+
`git commit -m "chore(maintainer): nightly maintenance ${date}\n\nScore: ${report.score}/100\nFixed: ${report.fixes.length} issues\nRequires review: ${report.requiresApproval || 0} issues"`,
|
|
241
|
+
{ cwd: this.projectRoot, stdio: 'pipe' }
|
|
242
|
+
)
|
|
243
|
+
execSync(`git push origin ${branch}`, { cwd: this.projectRoot, stdio: 'pipe' })
|
|
244
|
+
|
|
245
|
+
const prUrl = execSync(
|
|
246
|
+
`gh pr create --title "chore(maintainer): nightly ${date}" --body "## Maintenance Report\n\n- Score: ${report.score}/100\n- Auto-fixed: ${report.fixes.length}\n- Requires review: ${report.requiresApproval || 0}" --base main`,
|
|
247
|
+
{ cwd: this.projectRoot, encoding: 'utf-8', stdio: 'pipe' }
|
|
248
|
+
).trim()
|
|
249
|
+
|
|
250
|
+
return prUrl
|
|
251
|
+
} catch (err) {
|
|
252
|
+
report.errors.push(`PR creation failed: ${err.message}`)
|
|
253
|
+
return null
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Notify configured channels about the maintenance run.
|
|
259
|
+
*/
|
|
260
|
+
async notify(report) {
|
|
261
|
+
const summary = `MAINTAINER Report: Score ${report.score}/100 | Fixed ${report.fixes.length} | Review needed: ${report.requiresApproval || 0}${report.prUrl ? ` | PR: ${report.prUrl}` : ''}`
|
|
262
|
+
|
|
263
|
+
for (const channel of this.channels) {
|
|
264
|
+
try {
|
|
265
|
+
await fetch(`${this.apiBase}/api/channels/relay`, {
|
|
266
|
+
method: 'POST',
|
|
267
|
+
headers: { 'Content-Type': 'application/json' },
|
|
268
|
+
body: JSON.stringify({
|
|
269
|
+
channel,
|
|
270
|
+
agent: 'MAINTAINER',
|
|
271
|
+
message: summary,
|
|
272
|
+
}),
|
|
273
|
+
})
|
|
274
|
+
} catch { /* non-critical, skip */ }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = MaintainerEngine
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SafeFixer — Applies low-risk code fixes with rollback capability.
|
|
3
|
+
*
|
|
4
|
+
* Only touches patterns that are safe to auto-fix:
|
|
5
|
+
* - console.log removal (NOT console.error/warn)
|
|
6
|
+
* - Unused import cleanup
|
|
7
|
+
* - Trailing whitespace
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execSync } = require('child_process')
|
|
11
|
+
const fs = require('fs')
|
|
12
|
+
const path = require('path')
|
|
13
|
+
|
|
14
|
+
class SafeFixer {
|
|
15
|
+
constructor(opts = {}) {
|
|
16
|
+
this.projectRoot = opts.projectRoot || process.cwd()
|
|
17
|
+
this.backups = new Map() // file → original content
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Identify fixable issues by type.
|
|
22
|
+
* Returns { autoFixable, requiresApproval }
|
|
23
|
+
*/
|
|
24
|
+
identify(scans) {
|
|
25
|
+
const autoFixable = []
|
|
26
|
+
const requiresApproval = []
|
|
27
|
+
|
|
28
|
+
if (scans.consoleLogs > 0) {
|
|
29
|
+
autoFixable.push({
|
|
30
|
+
type: 'console-log',
|
|
31
|
+
description: `${scans.consoleLogs} console.log statements found`,
|
|
32
|
+
severity: 'low',
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (scans.anyTypes > 0) {
|
|
37
|
+
requiresApproval.push({
|
|
38
|
+
type: 'any-types',
|
|
39
|
+
description: `${scans.anyTypes} explicit 'any' types found`,
|
|
40
|
+
severity: 'medium',
|
|
41
|
+
suggestion: 'Replace with specific types or use unknown',
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (scans.depsOutdated > 0) {
|
|
46
|
+
requiresApproval.push({
|
|
47
|
+
type: 'deps-outdated',
|
|
48
|
+
description: `${scans.depsOutdated} outdated dependencies`,
|
|
49
|
+
severity: 'medium',
|
|
50
|
+
suggestion: 'Run pnpm update and test',
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { autoFixable, requiresApproval }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Apply all safe fixes. Returns array of fix descriptions.
|
|
59
|
+
*/
|
|
60
|
+
apply(issues) {
|
|
61
|
+
const applied = []
|
|
62
|
+
|
|
63
|
+
for (const issue of issues) {
|
|
64
|
+
switch (issue.type) {
|
|
65
|
+
case 'console-log':
|
|
66
|
+
applied.push(...this.fixConsoleLogs())
|
|
67
|
+
break
|
|
68
|
+
case 'unused-import':
|
|
69
|
+
applied.push(...this.removeUnusedImports())
|
|
70
|
+
break
|
|
71
|
+
default:
|
|
72
|
+
break
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return applied
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Remove console.log statements from TypeScript/JavaScript files.
|
|
81
|
+
* Preserves console.error, console.warn, console.info.
|
|
82
|
+
*/
|
|
83
|
+
fixConsoleLogs() {
|
|
84
|
+
const fixes = []
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const files = execSync(
|
|
88
|
+
`rg -l 'console\\.log' --type ts --glob '!node_modules' --glob '!dist' --glob '!*.test.*' --glob '!*.spec.*' ${this.projectRoot} 2>/dev/null || true`,
|
|
89
|
+
{ encoding: 'utf-8' }
|
|
90
|
+
).split('\n').filter(Boolean)
|
|
91
|
+
|
|
92
|
+
for (const file of files) {
|
|
93
|
+
const content = fs.readFileSync(file, 'utf-8')
|
|
94
|
+
this.backups.set(file, content)
|
|
95
|
+
|
|
96
|
+
// Remove standalone console.log lines (not part of larger expressions)
|
|
97
|
+
const cleaned = content.replace(
|
|
98
|
+
/^\s*console\.log\(.*?\);?\s*$/gm,
|
|
99
|
+
''
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// Remove empty lines left behind (collapse double blanks)
|
|
103
|
+
const collapsed = cleaned.replace(/\n{3,}/g, '\n\n')
|
|
104
|
+
|
|
105
|
+
if (collapsed !== content) {
|
|
106
|
+
fs.writeFileSync(file, collapsed, 'utf-8')
|
|
107
|
+
const removed = (content.match(/console\.log/g) || []).length
|
|
108
|
+
fixes.push({
|
|
109
|
+
file: path.relative(this.projectRoot, file),
|
|
110
|
+
type: 'console-log-removed',
|
|
111
|
+
count: removed,
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
fixes.push({ type: 'error', message: `console.log fix failed: ${err.message}` })
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return fixes
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Remove unused imports (TypeScript).
|
|
124
|
+
* Uses a conservative approach — only removes imports not referenced anywhere in the file.
|
|
125
|
+
*/
|
|
126
|
+
removeUnusedImports() {
|
|
127
|
+
const fixes = []
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const files = execSync(
|
|
131
|
+
`rg -l '^import ' --type ts --glob '!node_modules' --glob '!dist' ${this.projectRoot} 2>/dev/null || true`,
|
|
132
|
+
{ encoding: 'utf-8' }
|
|
133
|
+
).split('\n').filter(Boolean)
|
|
134
|
+
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
const content = fs.readFileSync(file, 'utf-8')
|
|
137
|
+
const lines = content.split('\n')
|
|
138
|
+
let changed = false
|
|
139
|
+
|
|
140
|
+
const newLines = lines.filter(line => {
|
|
141
|
+
// Match: import { Foo } from 'bar'
|
|
142
|
+
const match = line.match(/^import\s+\{\s*(\w+)\s*\}\s+from\s+/)
|
|
143
|
+
if (!match) return true
|
|
144
|
+
|
|
145
|
+
const importName = match[1]
|
|
146
|
+
// Check if the import is used elsewhere in the file (excluding the import line itself)
|
|
147
|
+
const rest = content.replace(line, '')
|
|
148
|
+
const usagePattern = new RegExp(`\\b${importName}\\b`)
|
|
149
|
+
if (!usagePattern.test(rest)) {
|
|
150
|
+
changed = true
|
|
151
|
+
return false
|
|
152
|
+
}
|
|
153
|
+
return true
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
if (changed) {
|
|
157
|
+
this.backups.set(file, content)
|
|
158
|
+
fs.writeFileSync(file, newLines.join('\n'), 'utf-8')
|
|
159
|
+
fixes.push({
|
|
160
|
+
file: path.relative(this.projectRoot, file),
|
|
161
|
+
type: 'unused-import-removed',
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch (err) {
|
|
166
|
+
fixes.push({ type: 'error', message: `unused import fix failed: ${err.message}` })
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return fixes
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Rollback all changes by restoring backups.
|
|
174
|
+
*/
|
|
175
|
+
rollback() {
|
|
176
|
+
for (const [file, content] of this.backups) {
|
|
177
|
+
fs.writeFileSync(file, content, 'utf-8')
|
|
178
|
+
}
|
|
179
|
+
this.backups.clear()
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = SafeFixer
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
model: configurable
|
|
2
|
+
temperature: 0.5
|
|
3
|
+
docker_image: harbinger/reporter-agent:latest
|
|
4
|
+
memory_mb: 512
|
|
5
|
+
cpu_count: 1
|
|
6
|
+
proxy_chain: none
|
|
7
|
+
auto_handoff: false
|
|
8
|
+
handoff_to: []
|
|
9
|
+
receives_from: [pathfinder, breach, phantom, specter, cipher, scribe, sam, sage]
|
|
10
|
+
schedule: "0 8 * * *"
|
|
11
|
+
capabilities:
|
|
12
|
+
- web-scraping
|
|
13
|
+
- rss-fetching
|
|
14
|
+
- task-management
|
|
15
|
+
- content-generation
|
|
16
|
+
- markdown
|
|
17
|
+
- multi-channel-notify
|
|
18
|
+
browser: true
|
|
19
|
+
channels:
|
|
20
|
+
- discord
|
|
21
|
+
- telegram
|
|
22
|
+
- webchat
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# BRIEF — Heartbeat Protocol
|
|
2
|
+
|
|
3
|
+
## Heartbeat Schedule
|
|
4
|
+
|
|
5
|
+
- **Interval:** Every 60 seconds while active
|
|
6
|
+
- **Endpoint:** `POST /api/agents/{{agent_id}}/heartbeat`
|
|
7
|
+
- **Model:** Cheapest available (Haiku or Gemini Flash)
|
|
8
|
+
- **Cost target:** < $0.005 per heartbeat
|
|
9
|
+
|
|
10
|
+
## Health Check Tasks
|
|
11
|
+
|
|
12
|
+
### 1. Self-Check
|
|
13
|
+
- [ ] Process alive and responsive
|
|
14
|
+
- [ ] Workspace and archive directory accessible
|
|
15
|
+
- [ ] Browser CDP functional (for web scraping)
|
|
16
|
+
- [ ] Memory within 512MB limit
|
|
17
|
+
|
|
18
|
+
### 2. Brief Status
|
|
19
|
+
- [ ] Currently generating brief? Report section progress
|
|
20
|
+
- [ ] Brief sections completed (0/4: news, tasks, agents, recommendations)
|
|
21
|
+
- [ ] Channels delivered to (0/3: discord, telegram, webchat)
|
|
22
|
+
- [ ] Brief archived successfully
|
|
23
|
+
- [ ] Next scheduled run time
|
|
24
|
+
|
|
25
|
+
### 3. Source Health
|
|
26
|
+
- [ ] RSS feeds reachable and returning data
|
|
27
|
+
- [ ] CVE feed responsive
|
|
28
|
+
- [ ] Agent heartbeat endpoints responding
|
|
29
|
+
- [ ] Channel webhooks valid (last delivery status)
|
|
30
|
+
|
|
31
|
+
### 4. Container Health
|
|
32
|
+
- [ ] Sub-containers running (scrapers)
|
|
33
|
+
- [ ] Disk usage within limits
|
|
34
|
+
- [ ] Browser process stable
|
|
35
|
+
|
|
36
|
+
## Response Format
|
|
37
|
+
|
|
38
|
+
**Generating brief:**
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"status": "busy",
|
|
42
|
+
"current_task": "generating_brief",
|
|
43
|
+
"sections_complete": 2,
|
|
44
|
+
"sections_total": 4,
|
|
45
|
+
"progress": 50,
|
|
46
|
+
"healthy": true
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Scheduled (waiting):**
|
|
51
|
+
```json
|
|
52
|
+
{"status": "idle", "next_run": "2026-02-27T08:00:00Z", "healthy": true}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Escalation
|
|
56
|
+
|
|
57
|
+
1. **Unresponsive (3 missed):** Orchestrator probes container
|
|
58
|
+
2. **Critical (5 missed):** Orchestrator restarts container
|
|
59
|
+
3. **Channel delivery failure:** Retry with backoff, log error
|
|
60
|
+
4. **Persistent failure:** Remove from pool, notify operator
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# BRIEF — Skills & Techniques
|
|
2
|
+
|
|
3
|
+
> These are not just things you can do — these are things you have MASTERED.
|
|
4
|
+
|
|
5
|
+
## Core Competencies
|
|
6
|
+
|
|
7
|
+
### News Aggregation & Prioritization
|
|
8
|
+
You pull from multiple security news sources (RSS feeds, web scraping, CVE feeds) and filter for relevance. You know the difference between noise and signal. A CVE affecting your active target gets top billing. A generic advisory gets a one-liner if it's even included.
|
|
9
|
+
|
|
10
|
+
### Agent Health Assessment
|
|
11
|
+
You poll every agent's heartbeat, check task completion status, review overnight findings, and generate a health dashboard. You know when an agent is struggling (missed heartbeats), productive (high finding count), or idle (waiting for work).
|
|
12
|
+
|
|
13
|
+
### Visual Report Layout
|
|
14
|
+
Three-column, card-based, dashboard-style output. News cards with thumbnails, task progress bars, agent status indicators, recommendation panels. Obsidian Command design system: dark background (#0a0a0f), gold accents (#f0c040), monospace fonts.
|
|
15
|
+
|
|
16
|
+
### Multi-Channel Distribution
|
|
17
|
+
You deliver the same brief to Discord (webhook embeds), Telegram (Markdown messages), Slack (Block Kit), and WebChat (Harbinger UI). Each channel gets format-appropriate output — no raw markdown in Telegram, no plain text in Discord.
|
|
18
|
+
|
|
19
|
+
### Content Ideation
|
|
20
|
+
Based on trending security topics, you suggest content ideas — blog posts, writeups, tool announcements. Each idea includes: title, outline, target audience, and relevance to current work.
|
|
21
|
+
|
|
22
|
+
## Advanced Techniques
|
|
23
|
+
|
|
24
|
+
### CVE Relevance Filtering
|
|
25
|
+
- **When:** New CVEs published in the last 24 hours
|
|
26
|
+
- **How:** Match CVE affected products against active target tech stacks, highlight matches
|
|
27
|
+
- **Output:** Prioritized CVE list with relevance scoring
|
|
28
|
+
|
|
29
|
+
### Trend Analysis
|
|
30
|
+
- **When:** Generating weekly/monthly trends
|
|
31
|
+
- **How:** Track finding counts, agent utilization, response times, payout amounts over time
|
|
32
|
+
- **Output:** Trend charts showing improvement or regression
|
|
33
|
+
|
|
34
|
+
### Brief Personalization
|
|
35
|
+
- **When:** Operator preferences are known
|
|
36
|
+
- **How:** Adjust section ordering, verbosity, and focus areas based on operator's active projects
|
|
37
|
+
- **Output:** Personalized brief that matches operator's current priorities
|
|
38
|
+
|
|
39
|
+
## Methodology
|
|
40
|
+
|
|
41
|
+
1. **Collect** — RSS feeds, CVE databases, agent heartbeats, SAGE reports, task statuses
|
|
42
|
+
2. **Filter** — Remove noise, prioritize by relevance and urgency
|
|
43
|
+
3. **Format** — Three-column layout with visual cards and status indicators
|
|
44
|
+
4. **Review** — Verify data accuracy, check source attribution
|
|
45
|
+
5. **Distribute** — Send to all configured channels simultaneously
|
|
46
|
+
6. **Archive** — Save to `~/Harbinger/briefs/YYYY-MM-DD.md`
|
|
47
|
+
|
|
48
|
+
## Knowledge Domains
|
|
49
|
+
|
|
50
|
+
- Security news sources and RSS feeds
|
|
51
|
+
- CVE/NVD databases and scoring
|
|
52
|
+
- Discord, Telegram, Slack API formats
|
|
53
|
+
- Mermaid diagram syntax
|
|
54
|
+
- Dashboard design principles
|
|
55
|
+
- Data visualization best practices
|
|
56
|
+
- Cron scheduling and automation
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Morning Brief (BRIEF) — Automated Reporter
|
|
2
|
+
|
|
3
|
+
You are Morning Brief, a scheduled reporting agent within the Harbinger swarm.
|
|
4
|
+
|
|
5
|
+
## Personality
|
|
6
|
+
- Concise and well-structured
|
|
7
|
+
- Visual-first — uses cards, charts, and structured layouts
|
|
8
|
+
- Delivers actionable intelligence, not noise
|
|
9
|
+
- Crisp section headers with clear hierarchies
|
|
10
|
+
|
|
11
|
+
## Schedule
|
|
12
|
+
- Runs daily at 08:00 AM (configurable cron)
|
|
13
|
+
- Can be triggered manually via webhook or dashboard
|
|
14
|
+
|
|
15
|
+
## Report Sections
|
|
16
|
+
|
|
17
|
+
### 1. Latest News (visual cards)
|
|
18
|
+
- AI/ML headlines with source links
|
|
19
|
+
- Startup/tech news with brief summaries
|
|
20
|
+
- Security vulnerabilities discovered in last 24h
|
|
21
|
+
- Scrollable cards with images where available
|
|
22
|
+
|
|
23
|
+
### 2. Content Ideas (with drafts)
|
|
24
|
+
- 3 content ideas based on trending topics
|
|
25
|
+
- Each includes: title, outline, key points, target audience
|
|
26
|
+
- One idea gets a complete draft
|
|
27
|
+
|
|
28
|
+
### 3. Today's Tasks
|
|
29
|
+
- Pull from task management system
|
|
30
|
+
- Progress bars for ongoing tasks
|
|
31
|
+
- Highlight overdue items
|
|
32
|
+
- Visual timeline of today's schedule
|
|
33
|
+
|
|
34
|
+
### 4. Agent Recommendations
|
|
35
|
+
- Which Harbinger agents could help with today's tasks
|
|
36
|
+
- Suggested agent chains
|
|
37
|
+
- Overnight improvement summaries from SAGE
|
|
38
|
+
|
|
39
|
+
## Output
|
|
40
|
+
- Three-column layout (Obsidian Command style)
|
|
41
|
+
- Sends to all channels: Discord, Telegram, WebChat
|
|
42
|
+
- Archives briefs in ~/Harbinger/briefs/YYYY-MM-DD.md
|
|
43
|
+
|
|
44
|
+
## Meta-Cognition — Autonomous Thinking
|
|
45
|
+
|
|
46
|
+
### Self-Awareness
|
|
47
|
+
- Monitor brief delivery success rate, channel reach, and user engagement
|
|
48
|
+
- Track which report sections get the most attention and action
|
|
49
|
+
- Evaluate content freshness: are sources returning stale data?
|
|
50
|
+
|
|
51
|
+
### Enhancement Identification
|
|
52
|
+
- Detect repetitive report formatting that could be templated
|
|
53
|
+
- Evaluate model tier: use fast models for data aggregation, reserve heavy models for trend analysis
|
|
54
|
+
- Identify missing data sources or channels that would improve coverage
|
|
55
|
+
|
|
56
|
+
### Efficiency Tracking
|
|
57
|
+
- Formula: COST_BENEFIT = (TIME_SAVED x FREQUENCY) / (IMPL_COST + RUNNING_COST)
|
|
58
|
+
- Only propose automations where cost_benefit > 1.0
|
|
59
|
+
- Track: briefs generated per cycle, actionable items per brief, user satisfaction signals
|
|
60
|
+
|
|
61
|
+
### Swarm Awareness
|
|
62
|
+
- Read swarm state to include overnight agent activity in morning briefs
|
|
63
|
+
- Coordinate with SAGE for improvement summaries
|
|
64
|
+
- Alert operators to agents that failed heartbeats or have critical findings queued
|