@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.
Files changed (317) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/agents/README.md +76 -0
  4. package/agents/_template/CONFIG.yaml +7 -0
  5. package/agents/_template/HEARTBEAT.md +59 -0
  6. package/agents/_template/IDENTITY.md +4 -0
  7. package/agents/_template/SKILLS.md +1 -0
  8. package/agents/_template/SOUL.md +25 -0
  9. package/agents/_template/TOOLS.md +3 -0
  10. package/agents/binary-reverser/CONFIG.yaml +21 -0
  11. package/agents/binary-reverser/HEARTBEAT.md +65 -0
  12. package/agents/binary-reverser/IDENTITY.md +1 -0
  13. package/agents/binary-reverser/SKILLS.md +1 -0
  14. package/agents/binary-reverser/SOUL.md +23 -0
  15. package/agents/binary-reverser/TOOLS.md +99 -0
  16. package/agents/browser-agent/CONFIG.yaml +20 -0
  17. package/agents/browser-agent/HEARTBEAT.md +79 -0
  18. package/agents/browser-agent/IDENTITY.md +5 -0
  19. package/agents/browser-agent/SKILLS.md +86 -0
  20. package/agents/browser-agent/SOUL.md +23 -0
  21. package/agents/browser-agent/TOOLS.md +186 -0
  22. package/agents/cloud-infiltrator/CONFIG.yaml +22 -0
  23. package/agents/cloud-infiltrator/HEARTBEAT.md +78 -0
  24. package/agents/cloud-infiltrator/IDENTITY.md +1 -0
  25. package/agents/cloud-infiltrator/SKILLS.md +1 -0
  26. package/agents/cloud-infiltrator/SOUL.md +23 -0
  27. package/agents/cloud-infiltrator/TOOLS.md +68 -0
  28. package/agents/coding-assistant/CONFIG.yaml +22 -0
  29. package/agents/coding-assistant/HEARTBEAT.md +57 -0
  30. package/agents/coding-assistant/IDENTITY.md +5 -0
  31. package/agents/coding-assistant/SKILLS.md +69 -0
  32. package/agents/coding-assistant/SOUL.md +60 -0
  33. package/agents/coding-assistant/TOOLS.md +168 -0
  34. package/agents/learning-agent/CONFIG.yaml +21 -0
  35. package/agents/learning-agent/HEARTBEAT.md +63 -0
  36. package/agents/learning-agent/IDENTITY.md +5 -0
  37. package/agents/learning-agent/SKILLS.md +86 -0
  38. package/agents/learning-agent/SOUL.md +77 -0
  39. package/agents/learning-agent/TOOLS.md +145 -0
  40. package/agents/maintainer/CONFIG.yaml +31 -0
  41. package/agents/maintainer/HEARTBEAT.md +28 -0
  42. package/agents/maintainer/IDENTITY.md +33 -0
  43. package/agents/maintainer/SKILLS.md +24 -0
  44. package/agents/maintainer/SOUL.md +61 -0
  45. package/agents/maintainer/TOOLS.md +29 -0
  46. package/agents/maintainer/lib/engine.js +279 -0
  47. package/agents/maintainer/lib/safe-fixer.js +183 -0
  48. package/agents/morning-brief/CONFIG.yaml +22 -0
  49. package/agents/morning-brief/HEARTBEAT.md +60 -0
  50. package/agents/morning-brief/IDENTITY.md +5 -0
  51. package/agents/morning-brief/SKILLS.md +56 -0
  52. package/agents/morning-brief/SOUL.md +64 -0
  53. package/agents/morning-brief/TOOLS.md +112 -0
  54. package/agents/osint-detective/CONFIG.yaml +24 -0
  55. package/agents/osint-detective/HEARTBEAT.md +66 -0
  56. package/agents/osint-detective/IDENTITY.md +1 -0
  57. package/agents/osint-detective/SKILLS.md +1 -0
  58. package/agents/osint-detective/SOUL.md +23 -0
  59. package/agents/osint-detective/TOOLS.md +81 -0
  60. package/agents/recon-scout/CONFIG.yaml +22 -0
  61. package/agents/recon-scout/HEARTBEAT.md +79 -0
  62. package/agents/recon-scout/IDENTITY.md +1 -0
  63. package/agents/recon-scout/SKILLS.md +1 -0
  64. package/agents/recon-scout/SOUL.md +23 -0
  65. package/agents/recon-scout/TOOLS.md +93 -0
  66. package/agents/report-writer/CONFIG.yaml +21 -0
  67. package/agents/report-writer/HEARTBEAT.md +63 -0
  68. package/agents/report-writer/IDENTITY.md +1 -0
  69. package/agents/report-writer/SKILLS.md +1 -0
  70. package/agents/report-writer/SOUL.md +23 -0
  71. package/agents/report-writer/TOOLS.md +69 -0
  72. package/agents/shared/README.md +13 -0
  73. package/agents/web-hacker/CONFIG.yaml +24 -0
  74. package/agents/web-hacker/HEARTBEAT.md +78 -0
  75. package/agents/web-hacker/IDENTITY.md +1 -0
  76. package/agents/web-hacker/SKILLS.md +1 -0
  77. package/agents/web-hacker/SOUL.md +23 -0
  78. package/agents/web-hacker/TOOLS.md +86 -0
  79. package/api/CLAUDE.md +19 -0
  80. package/api/index.js +274 -0
  81. package/bin/cli.js +620 -0
  82. package/bin/local.sh +31 -0
  83. package/bin/postinstall.js +63 -0
  84. package/config/index.js +24 -0
  85. package/config/instrumentation.js +93 -0
  86. package/drizzle/0000_initial.sql +52 -0
  87. package/drizzle/0001_bounty_and_registry.sql +82 -0
  88. package/drizzle/0002_sync_columns.sql +7 -0
  89. package/drizzle/0003_graceful_bloodscream.sql +86 -0
  90. package/drizzle/meta/0000_snapshot.json +321 -0
  91. package/drizzle/meta/0003_snapshot.json +878 -0
  92. package/drizzle/meta/_journal.json +34 -0
  93. package/drizzle/relations.ts +3 -0
  94. package/drizzle/schema.ts +145 -0
  95. package/lib/actions.js +47 -0
  96. package/lib/agents.js +166 -0
  97. package/lib/ai/agent.js +96 -0
  98. package/lib/ai/autonomous-engine.js +261 -0
  99. package/lib/ai/index.js +359 -0
  100. package/lib/ai/model-router.js +254 -0
  101. package/lib/ai/model.js +73 -0
  102. package/lib/ai/tools.js +84 -0
  103. package/lib/auth/actions.js +28 -0
  104. package/lib/auth/config.js +27 -0
  105. package/lib/auth/edge-config.js +27 -0
  106. package/lib/auth/index.js +27 -0
  107. package/lib/auth/middleware.js +53 -0
  108. package/lib/bounty/actions.js +119 -0
  109. package/lib/bounty/findings.js +64 -0
  110. package/lib/bounty/programs.js +34 -0
  111. package/lib/bounty/sync-targets.js +267 -0
  112. package/lib/bounty/targets.js +33 -0
  113. package/lib/channels/base.js +56 -0
  114. package/lib/channels/index.js +15 -0
  115. package/lib/channels/telegram.js +148 -0
  116. package/lib/chat/actions.js +288 -0
  117. package/lib/chat/api.js +135 -0
  118. package/lib/chat/components/app-sidebar.js +237 -0
  119. package/lib/chat/components/app-sidebar.jsx +289 -0
  120. package/lib/chat/components/chat-header.js +27 -0
  121. package/lib/chat/components/chat-header.jsx +37 -0
  122. package/lib/chat/components/chat-input.js +230 -0
  123. package/lib/chat/components/chat-input.jsx +228 -0
  124. package/lib/chat/components/chat-nav-context.js +11 -0
  125. package/lib/chat/components/chat-nav-context.jsx +11 -0
  126. package/lib/chat/components/chat-page.js +81 -0
  127. package/lib/chat/components/chat-page.jsx +100 -0
  128. package/lib/chat/components/chat.js +150 -0
  129. package/lib/chat/components/chat.jsx +182 -0
  130. package/lib/chat/components/chats-page.js +302 -0
  131. package/lib/chat/components/chats-page.jsx +330 -0
  132. package/lib/chat/components/crons-page.js +172 -0
  133. package/lib/chat/components/crons-page.jsx +244 -0
  134. package/lib/chat/components/enhanced-tool-call.js +103 -0
  135. package/lib/chat/components/enhanced-tool-call.jsx +139 -0
  136. package/lib/chat/components/findings-page.js +175 -0
  137. package/lib/chat/components/findings-page.jsx +214 -0
  138. package/lib/chat/components/greeting.js +22 -0
  139. package/lib/chat/components/greeting.jsx +26 -0
  140. package/lib/chat/components/icons.js +777 -0
  141. package/lib/chat/components/icons.jsx +741 -0
  142. package/lib/chat/components/index.js +26 -0
  143. package/lib/chat/components/mcp-page.js +260 -0
  144. package/lib/chat/components/mcp-page.jsx +355 -0
  145. package/lib/chat/components/message.js +289 -0
  146. package/lib/chat/components/message.jsx +315 -0
  147. package/lib/chat/components/messages.js +66 -0
  148. package/lib/chat/components/messages.jsx +77 -0
  149. package/lib/chat/components/notifications-page.js +56 -0
  150. package/lib/chat/components/notifications-page.jsx +87 -0
  151. package/lib/chat/components/page-layout.js +21 -0
  152. package/lib/chat/components/page-layout.jsx +28 -0
  153. package/lib/chat/components/registry-page.js +222 -0
  154. package/lib/chat/components/registry-page.jsx +255 -0
  155. package/lib/chat/components/settings-layout.js +40 -0
  156. package/lib/chat/components/settings-layout.jsx +54 -0
  157. package/lib/chat/components/settings-secrets-page.js +216 -0
  158. package/lib/chat/components/settings-secrets-page.jsx +264 -0
  159. package/lib/chat/components/sidebar-history-item.js +132 -0
  160. package/lib/chat/components/sidebar-history-item.jsx +113 -0
  161. package/lib/chat/components/sidebar-history.js +115 -0
  162. package/lib/chat/components/sidebar-history.jsx +157 -0
  163. package/lib/chat/components/sidebar-user-nav.js +63 -0
  164. package/lib/chat/components/sidebar-user-nav.jsx +73 -0
  165. package/lib/chat/components/status-bar.js +39 -0
  166. package/lib/chat/components/status-bar.jsx +51 -0
  167. package/lib/chat/components/swarm-page.js +157 -0
  168. package/lib/chat/components/swarm-page.jsx +210 -0
  169. package/lib/chat/components/targets-page.js +376 -0
  170. package/lib/chat/components/targets-page.jsx +389 -0
  171. package/lib/chat/components/tool-call.js +86 -0
  172. package/lib/chat/components/tool-call.jsx +104 -0
  173. package/lib/chat/components/tool-panel.js +107 -0
  174. package/lib/chat/components/tool-panel.jsx +145 -0
  175. package/lib/chat/components/triggers-page.js +153 -0
  176. package/lib/chat/components/triggers-page.jsx +221 -0
  177. package/lib/chat/components/ui/confirm-dialog.js +53 -0
  178. package/lib/chat/components/ui/confirm-dialog.jsx +57 -0
  179. package/lib/chat/components/ui/dropdown-menu.js +98 -0
  180. package/lib/chat/components/ui/dropdown-menu.jsx +116 -0
  181. package/lib/chat/components/ui/rename-dialog.js +74 -0
  182. package/lib/chat/components/ui/rename-dialog.jsx +72 -0
  183. package/lib/chat/components/ui/scroll-area.js +13 -0
  184. package/lib/chat/components/ui/scroll-area.jsx +17 -0
  185. package/lib/chat/components/ui/separator.js +21 -0
  186. package/lib/chat/components/ui/separator.jsx +18 -0
  187. package/lib/chat/components/ui/sheet.js +75 -0
  188. package/lib/chat/components/ui/sheet.jsx +95 -0
  189. package/lib/chat/components/ui/sidebar.js +227 -0
  190. package/lib/chat/components/ui/sidebar.jsx +245 -0
  191. package/lib/chat/components/ui/tooltip.js +56 -0
  192. package/lib/chat/components/ui/tooltip.jsx +66 -0
  193. package/lib/chat/components/upgrade-dialog.js +151 -0
  194. package/lib/chat/components/upgrade-dialog.jsx +170 -0
  195. package/lib/chat/utils.js +11 -0
  196. package/lib/cron.js +246 -0
  197. package/lib/db/api-keys.js +163 -0
  198. package/lib/db/chats.js +145 -0
  199. package/lib/db/index.js +52 -0
  200. package/lib/db/notifications.js +99 -0
  201. package/lib/db/schema.js +145 -0
  202. package/lib/db/update-check.js +96 -0
  203. package/lib/db/users.js +89 -0
  204. package/lib/mcp/actions.js +104 -0
  205. package/lib/mcp/client.js +79 -0
  206. package/lib/mcp/handler.js +57 -0
  207. package/lib/mcp/server.js +165 -0
  208. package/lib/paths.js +46 -0
  209. package/lib/registry/actions.js +164 -0
  210. package/lib/registry/catalog.js +137 -0
  211. package/lib/registry/tools.js +71 -0
  212. package/lib/tools/create-job.js +99 -0
  213. package/lib/tools/github.js +217 -0
  214. package/lib/tools/openai.js +35 -0
  215. package/lib/tools/telegram.js +292 -0
  216. package/lib/triggers.js +118 -0
  217. package/lib/utils/render-md.js +102 -0
  218. package/package.json +103 -0
  219. package/setup/lib/auth.mjs +81 -0
  220. package/setup/lib/env.mjs +21 -0
  221. package/setup/lib/fs-utils.mjs +20 -0
  222. package/setup/lib/github.mjs +149 -0
  223. package/setup/lib/prerequisites.mjs +155 -0
  224. package/setup/lib/prompts.mjs +267 -0
  225. package/setup/lib/providers.mjs +48 -0
  226. package/setup/lib/sync.mjs +125 -0
  227. package/setup/lib/targets.mjs +45 -0
  228. package/setup/lib/telegram-verify.mjs +63 -0
  229. package/setup/lib/telegram.mjs +76 -0
  230. package/setup/setup-telegram.mjs +264 -0
  231. package/setup/setup.mjs +842 -0
  232. package/templates/.dockerignore +5 -0
  233. package/templates/.env.example +63 -0
  234. package/templates/.github/workflows/auto-merge.yml +117 -0
  235. package/templates/.github/workflows/build-image.yml +36 -0
  236. package/templates/.github/workflows/notify-job-failed.yml +64 -0
  237. package/templates/.github/workflows/notify-pr-complete.yml +119 -0
  238. package/templates/.github/workflows/rebuild-event-handler.yml +121 -0
  239. package/templates/.github/workflows/run-job.yml +89 -0
  240. package/templates/.github/workflows/upgrade-event-handler.yml +62 -0
  241. package/templates/.gitignore.template +45 -0
  242. package/templates/.pi/extensions/env-sanitizer/index.ts +48 -0
  243. package/templates/.pi/extensions/env-sanitizer/package.json +5 -0
  244. package/templates/CLAUDE.md +29 -0
  245. package/templates/CLAUDE.md.template +307 -0
  246. package/templates/app/api/[...thepopebot]/route.js +1 -0
  247. package/templates/app/api/auth/[...nextauth]/route.js +1 -0
  248. package/templates/app/chat/[chatId]/page.js +8 -0
  249. package/templates/app/chats/page.js +7 -0
  250. package/templates/app/components/ascii-logo.jsx +10 -0
  251. package/templates/app/components/login-form.jsx +92 -0
  252. package/templates/app/components/setup-form.jsx +82 -0
  253. package/templates/app/components/theme-provider.jsx +11 -0
  254. package/templates/app/components/theme-toggle.jsx +38 -0
  255. package/templates/app/components/ui/button.jsx +21 -0
  256. package/templates/app/components/ui/card.jsx +23 -0
  257. package/templates/app/components/ui/input.jsx +10 -0
  258. package/templates/app/components/ui/label.jsx +10 -0
  259. package/templates/app/crons/page.js +5 -0
  260. package/templates/app/findings/page.js +7 -0
  261. package/templates/app/globals.css +90 -0
  262. package/templates/app/layout.js +19 -0
  263. package/templates/app/login/page.js +15 -0
  264. package/templates/app/notifications/page.js +7 -0
  265. package/templates/app/page.js +7 -0
  266. package/templates/app/settings/crons/page.js +5 -0
  267. package/templates/app/settings/layout.js +7 -0
  268. package/templates/app/settings/mcp/page.js +5 -0
  269. package/templates/app/settings/page.js +5 -0
  270. package/templates/app/settings/secrets/page.js +5 -0
  271. package/templates/app/settings/triggers/page.js +5 -0
  272. package/templates/app/stream/chat/route.js +1 -0
  273. package/templates/app/swarm/page.js +7 -0
  274. package/templates/app/targets/page.js +7 -0
  275. package/templates/app/toolbox/page.js +7 -0
  276. package/templates/app/triggers/page.js +5 -0
  277. package/templates/config/AGENT.md +34 -0
  278. package/templates/config/CRONS.json +56 -0
  279. package/templates/config/EVENT_HANDLER.md +224 -0
  280. package/templates/config/HEARTBEAT.md +3 -0
  281. package/templates/config/JOB_SUMMARY.md +130 -0
  282. package/templates/config/MCP_SERVERS.json +1 -0
  283. package/templates/config/SKILL_BUILDING_GUIDE.md +90 -0
  284. package/templates/config/SOUL.md +17 -0
  285. package/templates/config/TRIGGERS.json +58 -0
  286. package/templates/docker/event-handler/Dockerfile +20 -0
  287. package/templates/docker/event-handler/ecosystem.config.cjs +8 -0
  288. package/templates/docker/job-claude-code/Dockerfile +34 -0
  289. package/templates/docker/job-claude-code/entrypoint.sh +139 -0
  290. package/templates/docker/job-pi-coding-agent/Dockerfile +44 -0
  291. package/templates/docker/job-pi-coding-agent/entrypoint.sh +163 -0
  292. package/templates/docker-compose.yml +63 -0
  293. package/templates/instrumentation.js +6 -0
  294. package/templates/middleware.js +1 -0
  295. package/templates/next.config.mjs +3 -0
  296. package/templates/postcss.config.mjs +5 -0
  297. package/templates/skills/LICENSE +21 -0
  298. package/templates/skills/README.md +119 -0
  299. package/templates/skills/brave-search/SKILL.md +79 -0
  300. package/templates/skills/brave-search/content.js +86 -0
  301. package/templates/skills/brave-search/package-lock.json +621 -0
  302. package/templates/skills/brave-search/package.json +14 -0
  303. package/templates/skills/brave-search/search.js +199 -0
  304. package/templates/skills/browser-tools/SKILL.md +196 -0
  305. package/templates/skills/browser-tools/browser-content.js +103 -0
  306. package/templates/skills/browser-tools/browser-cookies.js +35 -0
  307. package/templates/skills/browser-tools/browser-eval.js +53 -0
  308. package/templates/skills/browser-tools/browser-hn-scraper.js +108 -0
  309. package/templates/skills/browser-tools/browser-nav.js +44 -0
  310. package/templates/skills/browser-tools/browser-pick.js +162 -0
  311. package/templates/skills/browser-tools/browser-screenshot.js +34 -0
  312. package/templates/skills/browser-tools/browser-start.js +87 -0
  313. package/templates/skills/browser-tools/package-lock.json +2556 -0
  314. package/templates/skills/browser-tools/package.json +19 -0
  315. package/templates/skills/llm-secrets/SKILL.md +34 -0
  316. package/templates/skills/llm-secrets/llm-secrets.js +33 -0
  317. 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,5 @@
1
+ Name: Morning Brief
2
+ Codename: BRIEF
3
+ Role: Automated Reporter
4
+ Specialization: Daily visual reports — news, content ideas, task summaries, agent recommendations
5
+ Color: #f0c040
@@ -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