@timmeck/marketing-brain 0.2.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 (294) hide show
  1. package/.mcp.json +9 -0
  2. package/README.md +342 -0
  3. package/dashboard.html +666 -0
  4. package/dist/api/server.d.ts +15 -0
  5. package/dist/api/server.js +73 -0
  6. package/dist/api/server.js.map +1 -0
  7. package/dist/cli/colors.d.ts +43 -0
  8. package/dist/cli/colors.js +54 -0
  9. package/dist/cli/colors.js.map +1 -0
  10. package/dist/cli/commands/campaign.d.ts +2 -0
  11. package/dist/cli/commands/campaign.js +62 -0
  12. package/dist/cli/commands/campaign.js.map +1 -0
  13. package/dist/cli/commands/config.d.ts +2 -0
  14. package/dist/cli/commands/config.js +164 -0
  15. package/dist/cli/commands/config.js.map +1 -0
  16. package/dist/cli/commands/dashboard.d.ts +2 -0
  17. package/dist/cli/commands/dashboard.js +147 -0
  18. package/dist/cli/commands/dashboard.js.map +1 -0
  19. package/dist/cli/commands/doctor.d.ts +2 -0
  20. package/dist/cli/commands/doctor.js +111 -0
  21. package/dist/cli/commands/doctor.js.map +1 -0
  22. package/dist/cli/commands/export.d.ts +2 -0
  23. package/dist/cli/commands/export.js +37 -0
  24. package/dist/cli/commands/export.js.map +1 -0
  25. package/dist/cli/commands/import.d.ts +2 -0
  26. package/dist/cli/commands/import.js +76 -0
  27. package/dist/cli/commands/import.js.map +1 -0
  28. package/dist/cli/commands/insights.d.ts +2 -0
  29. package/dist/cli/commands/insights.js +41 -0
  30. package/dist/cli/commands/insights.js.map +1 -0
  31. package/dist/cli/commands/learn.d.ts +2 -0
  32. package/dist/cli/commands/learn.js +22 -0
  33. package/dist/cli/commands/learn.js.map +1 -0
  34. package/dist/cli/commands/network.d.ts +2 -0
  35. package/dist/cli/commands/network.js +66 -0
  36. package/dist/cli/commands/network.js.map +1 -0
  37. package/dist/cli/commands/post.d.ts +2 -0
  38. package/dist/cli/commands/post.js +45 -0
  39. package/dist/cli/commands/post.js.map +1 -0
  40. package/dist/cli/commands/query.d.ts +2 -0
  41. package/dist/cli/commands/query.js +96 -0
  42. package/dist/cli/commands/query.js.map +1 -0
  43. package/dist/cli/commands/rules.d.ts +2 -0
  44. package/dist/cli/commands/rules.js +25 -0
  45. package/dist/cli/commands/rules.js.map +1 -0
  46. package/dist/cli/commands/start.d.ts +2 -0
  47. package/dist/cli/commands/start.js +91 -0
  48. package/dist/cli/commands/start.js.map +1 -0
  49. package/dist/cli/commands/status.d.ts +2 -0
  50. package/dist/cli/commands/status.js +63 -0
  51. package/dist/cli/commands/status.js.map +1 -0
  52. package/dist/cli/commands/stop.d.ts +2 -0
  53. package/dist/cli/commands/stop.js +34 -0
  54. package/dist/cli/commands/stop.js.map +1 -0
  55. package/dist/cli/commands/suggest.d.ts +2 -0
  56. package/dist/cli/commands/suggest.js +57 -0
  57. package/dist/cli/commands/suggest.js.map +1 -0
  58. package/dist/cli/ipc-helper.d.ts +2 -0
  59. package/dist/cli/ipc-helper.js +26 -0
  60. package/dist/cli/ipc-helper.js.map +1 -0
  61. package/dist/cli/update-check.d.ts +2 -0
  62. package/dist/cli/update-check.js +58 -0
  63. package/dist/cli/update-check.js.map +1 -0
  64. package/dist/config.d.ts +2 -0
  65. package/dist/config.js +111 -0
  66. package/dist/config.js.map +1 -0
  67. package/dist/dashboard/renderer.d.ts +11 -0
  68. package/dist/dashboard/renderer.js +112 -0
  69. package/dist/dashboard/renderer.js.map +1 -0
  70. package/dist/dashboard/server.d.ts +15 -0
  71. package/dist/dashboard/server.js +122 -0
  72. package/dist/dashboard/server.js.map +1 -0
  73. package/dist/db/connection.d.ts +2 -0
  74. package/dist/db/connection.js +19 -0
  75. package/dist/db/connection.js.map +1 -0
  76. package/dist/db/migrations/001_core_schema.d.ts +2 -0
  77. package/dist/db/migrations/001_core_schema.js +62 -0
  78. package/dist/db/migrations/001_core_schema.js.map +1 -0
  79. package/dist/db/migrations/002_learning_schema.d.ts +2 -0
  80. package/dist/db/migrations/002_learning_schema.js +45 -0
  81. package/dist/db/migrations/002_learning_schema.js.map +1 -0
  82. package/dist/db/migrations/003_synapse_schema.d.ts +2 -0
  83. package/dist/db/migrations/003_synapse_schema.js +26 -0
  84. package/dist/db/migrations/003_synapse_schema.js.map +1 -0
  85. package/dist/db/migrations/004_insights_schema.d.ts +2 -0
  86. package/dist/db/migrations/004_insights_schema.js +37 -0
  87. package/dist/db/migrations/004_insights_schema.js.map +1 -0
  88. package/dist/db/migrations/005_fts_indexes.d.ts +2 -0
  89. package/dist/db/migrations/005_fts_indexes.js +76 -0
  90. package/dist/db/migrations/005_fts_indexes.js.map +1 -0
  91. package/dist/db/migrations/index.d.ts +2 -0
  92. package/dist/db/migrations/index.js +47 -0
  93. package/dist/db/migrations/index.js.map +1 -0
  94. package/dist/db/repositories/audience.repository.d.ts +18 -0
  95. package/dist/db/repositories/audience.repository.js +45 -0
  96. package/dist/db/repositories/audience.repository.js.map +1 -0
  97. package/dist/db/repositories/campaign.repository.d.ts +15 -0
  98. package/dist/db/repositories/campaign.repository.js +58 -0
  99. package/dist/db/repositories/campaign.repository.js.map +1 -0
  100. package/dist/db/repositories/engagement.repository.d.ts +26 -0
  101. package/dist/db/repositories/engagement.repository.js +83 -0
  102. package/dist/db/repositories/engagement.repository.js.map +1 -0
  103. package/dist/db/repositories/insight.repository.d.ts +18 -0
  104. package/dist/db/repositories/insight.repository.js +87 -0
  105. package/dist/db/repositories/insight.repository.js.map +1 -0
  106. package/dist/db/repositories/post.repository.d.ts +21 -0
  107. package/dist/db/repositories/post.repository.js +105 -0
  108. package/dist/db/repositories/post.repository.js.map +1 -0
  109. package/dist/db/repositories/rule.repository.d.ts +16 -0
  110. package/dist/db/repositories/rule.repository.js +71 -0
  111. package/dist/db/repositories/rule.repository.js.map +1 -0
  112. package/dist/db/repositories/strategy.repository.d.ts +16 -0
  113. package/dist/db/repositories/strategy.repository.js +69 -0
  114. package/dist/db/repositories/strategy.repository.js.map +1 -0
  115. package/dist/db/repositories/synapse.repository.d.ts +25 -0
  116. package/dist/db/repositories/synapse.repository.js +115 -0
  117. package/dist/db/repositories/synapse.repository.js.map +1 -0
  118. package/dist/db/repositories/template.repository.d.ts +16 -0
  119. package/dist/db/repositories/template.repository.js +61 -0
  120. package/dist/db/repositories/template.repository.js.map +1 -0
  121. package/dist/index.d.ts +2 -0
  122. package/dist/index.js +62 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/ipc/client.d.ts +13 -0
  125. package/dist/ipc/client.js +93 -0
  126. package/dist/ipc/client.js.map +1 -0
  127. package/dist/ipc/protocol.d.ts +8 -0
  128. package/dist/ipc/protocol.js +29 -0
  129. package/dist/ipc/protocol.js.map +1 -0
  130. package/dist/ipc/router.d.ts +30 -0
  131. package/dist/ipc/router.js +88 -0
  132. package/dist/ipc/router.js.map +1 -0
  133. package/dist/ipc/server.d.ts +14 -0
  134. package/dist/ipc/server.js +130 -0
  135. package/dist/ipc/server.js.map +1 -0
  136. package/dist/learning/confidence-scorer.d.ts +17 -0
  137. package/dist/learning/confidence-scorer.js +26 -0
  138. package/dist/learning/confidence-scorer.js.map +1 -0
  139. package/dist/learning/learning-engine.d.ts +33 -0
  140. package/dist/learning/learning-engine.js +211 -0
  141. package/dist/learning/learning-engine.js.map +1 -0
  142. package/dist/marketing-core.d.ts +17 -0
  143. package/dist/marketing-core.js +233 -0
  144. package/dist/marketing-core.js.map +1 -0
  145. package/dist/mcp/server.d.ts +1 -0
  146. package/dist/mcp/server.js +67 -0
  147. package/dist/mcp/server.js.map +1 -0
  148. package/dist/mcp/tools.d.ts +3 -0
  149. package/dist/mcp/tools.js +138 -0
  150. package/dist/mcp/tools.js.map +1 -0
  151. package/dist/research/research-engine.d.ts +28 -0
  152. package/dist/research/research-engine.js +211 -0
  153. package/dist/research/research-engine.js.map +1 -0
  154. package/dist/services/analytics.service.d.ts +116 -0
  155. package/dist/services/analytics.service.js +69 -0
  156. package/dist/services/analytics.service.js.map +1 -0
  157. package/dist/services/audience.service.d.ts +20 -0
  158. package/dist/services/audience.service.js +30 -0
  159. package/dist/services/audience.service.js.map +1 -0
  160. package/dist/services/campaign.service.d.ts +27 -0
  161. package/dist/services/campaign.service.js +65 -0
  162. package/dist/services/campaign.service.js.map +1 -0
  163. package/dist/services/insight.service.d.ts +18 -0
  164. package/dist/services/insight.service.js +40 -0
  165. package/dist/services/insight.service.js.map +1 -0
  166. package/dist/services/post.service.d.ts +48 -0
  167. package/dist/services/post.service.js +93 -0
  168. package/dist/services/post.service.js.map +1 -0
  169. package/dist/services/rule.service.d.ts +29 -0
  170. package/dist/services/rule.service.js +67 -0
  171. package/dist/services/rule.service.js.map +1 -0
  172. package/dist/services/strategy.service.d.ts +17 -0
  173. package/dist/services/strategy.service.js +39 -0
  174. package/dist/services/strategy.service.js.map +1 -0
  175. package/dist/services/synapse.service.d.ts +22 -0
  176. package/dist/services/synapse.service.js +22 -0
  177. package/dist/services/synapse.service.js.map +1 -0
  178. package/dist/services/template.service.d.ts +17 -0
  179. package/dist/services/template.service.js +37 -0
  180. package/dist/services/template.service.js.map +1 -0
  181. package/dist/synapses/activation.d.ts +13 -0
  182. package/dist/synapses/activation.js +50 -0
  183. package/dist/synapses/activation.js.map +1 -0
  184. package/dist/synapses/decay.d.ts +11 -0
  185. package/dist/synapses/decay.js +27 -0
  186. package/dist/synapses/decay.js.map +1 -0
  187. package/dist/synapses/hebbian.d.ts +13 -0
  188. package/dist/synapses/hebbian.js +35 -0
  189. package/dist/synapses/hebbian.js.map +1 -0
  190. package/dist/synapses/pathfinder.d.ts +14 -0
  191. package/dist/synapses/pathfinder.js +50 -0
  192. package/dist/synapses/pathfinder.js.map +1 -0
  193. package/dist/synapses/synapse-manager.d.ts +32 -0
  194. package/dist/synapses/synapse-manager.js +76 -0
  195. package/dist/synapses/synapse-manager.js.map +1 -0
  196. package/dist/types/config.types.d.ts +69 -0
  197. package/dist/types/config.types.js +2 -0
  198. package/dist/types/config.types.js.map +1 -0
  199. package/dist/types/ipc.types.d.ts +11 -0
  200. package/dist/types/ipc.types.js +2 -0
  201. package/dist/types/ipc.types.js.map +1 -0
  202. package/dist/types/post.types.d.ts +141 -0
  203. package/dist/types/post.types.js +2 -0
  204. package/dist/types/post.types.js.map +1 -0
  205. package/dist/types/synapse.types.d.ts +23 -0
  206. package/dist/types/synapse.types.js +2 -0
  207. package/dist/types/synapse.types.js.map +1 -0
  208. package/dist/utils/events.d.ts +57 -0
  209. package/dist/utils/events.js +23 -0
  210. package/dist/utils/events.js.map +1 -0
  211. package/dist/utils/hash.d.ts +1 -0
  212. package/dist/utils/hash.js +5 -0
  213. package/dist/utils/hash.js.map +1 -0
  214. package/dist/utils/logger.d.ts +8 -0
  215. package/dist/utils/logger.js +39 -0
  216. package/dist/utils/logger.js.map +1 -0
  217. package/dist/utils/paths.d.ts +3 -0
  218. package/dist/utils/paths.js +18 -0
  219. package/dist/utils/paths.js.map +1 -0
  220. package/package.json +40 -0
  221. package/seed-data.json +78 -0
  222. package/src/api/server.ts +86 -0
  223. package/src/cli/colors.ts +59 -0
  224. package/src/cli/commands/campaign.ts +66 -0
  225. package/src/cli/commands/config.ts +168 -0
  226. package/src/cli/commands/dashboard.ts +165 -0
  227. package/src/cli/commands/doctor.ts +110 -0
  228. package/src/cli/commands/export.ts +40 -0
  229. package/src/cli/commands/import.ts +84 -0
  230. package/src/cli/commands/insights.ts +44 -0
  231. package/src/cli/commands/learn.ts +24 -0
  232. package/src/cli/commands/network.ts +71 -0
  233. package/src/cli/commands/post.ts +47 -0
  234. package/src/cli/commands/query.ts +108 -0
  235. package/src/cli/commands/rules.ts +27 -0
  236. package/src/cli/commands/start.ts +100 -0
  237. package/src/cli/commands/status.ts +73 -0
  238. package/src/cli/commands/stop.ts +33 -0
  239. package/src/cli/commands/suggest.ts +64 -0
  240. package/src/cli/ipc-helper.ts +22 -0
  241. package/src/cli/update-check.ts +63 -0
  242. package/src/config.ts +110 -0
  243. package/src/dashboard/renderer.ts +136 -0
  244. package/src/dashboard/server.ts +140 -0
  245. package/src/db/connection.ts +22 -0
  246. package/src/db/migrations/001_core_schema.ts +63 -0
  247. package/src/db/migrations/002_learning_schema.ts +46 -0
  248. package/src/db/migrations/003_synapse_schema.ts +27 -0
  249. package/src/db/migrations/004_insights_schema.ts +38 -0
  250. package/src/db/migrations/005_fts_indexes.ts +77 -0
  251. package/src/db/migrations/index.ts +62 -0
  252. package/src/db/repositories/audience.repository.ts +53 -0
  253. package/src/db/repositories/campaign.repository.ts +72 -0
  254. package/src/db/repositories/engagement.repository.ts +108 -0
  255. package/src/db/repositories/insight.repository.ts +100 -0
  256. package/src/db/repositories/post.repository.ts +123 -0
  257. package/src/db/repositories/rule.repository.ts +87 -0
  258. package/src/db/repositories/strategy.repository.ts +82 -0
  259. package/src/db/repositories/synapse.repository.ts +148 -0
  260. package/src/db/repositories/template.repository.ts +76 -0
  261. package/src/index.ts +69 -0
  262. package/src/ipc/client.ts +110 -0
  263. package/src/ipc/protocol.ts +35 -0
  264. package/src/ipc/router.ts +126 -0
  265. package/src/ipc/server.ts +140 -0
  266. package/src/learning/confidence-scorer.ts +36 -0
  267. package/src/learning/learning-engine.ts +254 -0
  268. package/src/marketing-core.ts +285 -0
  269. package/src/mcp/server.ts +72 -0
  270. package/src/mcp/tools.ts +216 -0
  271. package/src/research/research-engine.ts +226 -0
  272. package/src/services/analytics.service.ts +73 -0
  273. package/src/services/audience.service.ts +40 -0
  274. package/src/services/campaign.service.ts +80 -0
  275. package/src/services/insight.service.ts +54 -0
  276. package/src/services/post.service.ts +116 -0
  277. package/src/services/rule.service.ts +90 -0
  278. package/src/services/strategy.service.ts +53 -0
  279. package/src/services/synapse.service.ts +32 -0
  280. package/src/services/template.service.ts +50 -0
  281. package/src/synapses/activation.ts +80 -0
  282. package/src/synapses/decay.ts +38 -0
  283. package/src/synapses/hebbian.ts +68 -0
  284. package/src/synapses/pathfinder.ts +81 -0
  285. package/src/synapses/synapse-manager.ts +115 -0
  286. package/src/types/config.types.ts +79 -0
  287. package/src/types/ipc.types.ts +8 -0
  288. package/src/types/post.types.ts +156 -0
  289. package/src/types/synapse.types.ts +43 -0
  290. package/src/utils/events.ts +44 -0
  291. package/src/utils/hash.ts +5 -0
  292. package/src/utils/logger.ts +48 -0
  293. package/src/utils/paths.ts +19 -0
  294. package/tsconfig.json +18 -0
@@ -0,0 +1,285 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import type Database from 'better-sqlite3';
4
+ import { loadConfig } from './config.js';
5
+ import type { MarketingBrainConfig } from './types/config.types.js';
6
+ import { createLogger, getLogger } from './utils/logger.js';
7
+ import { getEventBus } from './utils/events.js';
8
+ import { createConnection } from './db/connection.js';
9
+ import { runMigrations } from './db/migrations/index.js';
10
+
11
+ // Repositories
12
+ import { PostRepository } from './db/repositories/post.repository.js';
13
+ import { EngagementRepository } from './db/repositories/engagement.repository.js';
14
+ import { CampaignRepository } from './db/repositories/campaign.repository.js';
15
+ import { StrategyRepository } from './db/repositories/strategy.repository.js';
16
+ import { RuleRepository } from './db/repositories/rule.repository.js';
17
+ import { TemplateRepository } from './db/repositories/template.repository.js';
18
+ import { AudienceRepository } from './db/repositories/audience.repository.js';
19
+ import { SynapseRepository } from './db/repositories/synapse.repository.js';
20
+ import { InsightRepository } from './db/repositories/insight.repository.js';
21
+
22
+ // Services
23
+ import { PostService } from './services/post.service.js';
24
+ import { CampaignService } from './services/campaign.service.js';
25
+ import { StrategyService } from './services/strategy.service.js';
26
+ import { TemplateService } from './services/template.service.js';
27
+ import { RuleService } from './services/rule.service.js';
28
+ import { AudienceService } from './services/audience.service.js';
29
+ import { SynapseService } from './services/synapse.service.js';
30
+ import { AnalyticsService } from './services/analytics.service.js';
31
+ import { InsightService } from './services/insight.service.js';
32
+
33
+ // Synapses
34
+ import { SynapseManager } from './synapses/synapse-manager.js';
35
+
36
+ // Engines
37
+ import { LearningEngine } from './learning/learning-engine.js';
38
+ import { ResearchEngine } from './research/research-engine.js';
39
+
40
+ // IPC
41
+ import { IpcRouter, type Services } from './ipc/router.js';
42
+ import { IpcServer } from './ipc/server.js';
43
+
44
+ // API
45
+ import { ApiServer } from './api/server.js';
46
+
47
+ // Dashboard
48
+ import { DashboardServer } from './dashboard/server.js';
49
+ import { renderDashboard } from './dashboard/renderer.js';
50
+
51
+ export class MarketingCore {
52
+ private db: Database.Database | null = null;
53
+ private ipcServer: IpcServer | null = null;
54
+ private apiServer: ApiServer | null = null;
55
+ private dashboardServer: DashboardServer | null = null;
56
+ private learningEngine: LearningEngine | null = null;
57
+ private researchEngine: ResearchEngine | null = null;
58
+ private config: MarketingBrainConfig | null = null;
59
+ private configPath?: string;
60
+ private restarting = false;
61
+
62
+ start(configPath?: string): void {
63
+ this.configPath = configPath;
64
+ // 1. Config
65
+ this.config = loadConfig(configPath);
66
+ const config = this.config;
67
+
68
+ // 2. Ensure data dir
69
+ fs.mkdirSync(path.dirname(config.dbPath), { recursive: true });
70
+
71
+ // 3. Logger
72
+ createLogger({
73
+ level: config.log.level,
74
+ file: config.log.file,
75
+ maxSize: config.log.maxSize,
76
+ maxFiles: config.log.maxFiles,
77
+ });
78
+ const logger = getLogger();
79
+
80
+ // 4. Database
81
+ this.db = createConnection(config.dbPath);
82
+ runMigrations(this.db);
83
+ logger.info(`Database initialized: ${config.dbPath}`);
84
+
85
+ // 5. Repositories
86
+ const postRepo = new PostRepository(this.db);
87
+ const engagementRepo = new EngagementRepository(this.db);
88
+ const campaignRepo = new CampaignRepository(this.db);
89
+ const strategyRepo = new StrategyRepository(this.db);
90
+ const ruleRepo = new RuleRepository(this.db);
91
+ const templateRepo = new TemplateRepository(this.db);
92
+ const audienceRepo = new AudienceRepository(this.db);
93
+ const synapseRepo = new SynapseRepository(this.db);
94
+ const insightRepo = new InsightRepository(this.db);
95
+
96
+ // 6. Synapse Manager
97
+ const synapseManager = new SynapseManager(synapseRepo, config.synapses);
98
+
99
+ // 7. Services
100
+ const services: Services = {
101
+ post: new PostService(postRepo, engagementRepo, synapseManager),
102
+ campaign: new CampaignService(campaignRepo, postRepo, engagementRepo, synapseManager),
103
+ strategy: new StrategyService(strategyRepo, synapseManager),
104
+ template: new TemplateService(templateRepo, synapseManager),
105
+ rule: new RuleService(ruleRepo, synapseManager),
106
+ audience: new AudienceService(audienceRepo, synapseManager),
107
+ synapse: new SynapseService(synapseManager),
108
+ analytics: new AnalyticsService(
109
+ postRepo, engagementRepo, campaignRepo,
110
+ strategyRepo, ruleRepo, templateRepo,
111
+ insightRepo, synapseManager,
112
+ ),
113
+ insight: new InsightService(insightRepo, synapseManager),
114
+ };
115
+
116
+ // 8. Learning Engine
117
+ this.learningEngine = new LearningEngine(
118
+ config.learning, postRepo, engagementRepo,
119
+ ruleRepo, strategyRepo, synapseManager,
120
+ );
121
+ this.learningEngine.start();
122
+ logger.info(`Learning engine started (interval: ${config.learning.intervalMs}ms)`);
123
+
124
+ // 9. Research Engine
125
+ this.researchEngine = new ResearchEngine(
126
+ config.research, postRepo, engagementRepo,
127
+ campaignRepo, templateRepo, insightRepo, synapseManager,
128
+ );
129
+ this.researchEngine.start();
130
+ logger.info(`Research engine started (interval: ${config.research.intervalMs}ms)`);
131
+
132
+ // Expose learning engine to IPC
133
+ services.learning = this.learningEngine;
134
+
135
+ // 10. IPC Server
136
+ const router = new IpcRouter(services);
137
+ this.ipcServer = new IpcServer(router, config.ipc.pipeName);
138
+ this.ipcServer.start();
139
+
140
+ // 11. REST API Server
141
+ if (config.api.enabled) {
142
+ this.apiServer = new ApiServer({
143
+ port: config.api.port,
144
+ router,
145
+ apiKey: config.api.apiKey,
146
+ });
147
+ this.apiServer.start();
148
+ logger.info(`REST API enabled on port ${config.api.port}`);
149
+ }
150
+
151
+ // 12. Dashboard Server (SSE)
152
+ if (config.dashboard.enabled) {
153
+ const dashboardHtmlPath = path.resolve(
154
+ path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')),
155
+ '../dashboard.html',
156
+ );
157
+ const dashServices = {
158
+ analytics: services.analytics,
159
+ insight: services.insight,
160
+ rule: services.rule,
161
+ synapse: services.synapse,
162
+ };
163
+ this.dashboardServer = new DashboardServer({
164
+ port: config.dashboard.port,
165
+ getDashboardHtml: () => {
166
+ try {
167
+ const template = fs.readFileSync(dashboardHtmlPath, 'utf-8');
168
+ return renderDashboard(template, dashServices);
169
+ } catch {
170
+ return '<html><body><h1>Dashboard HTML not found</h1></body></html>';
171
+ }
172
+ },
173
+ getStats: () => services.analytics.getSummary(),
174
+ });
175
+ this.dashboardServer.start();
176
+ logger.info(`Dashboard server enabled on port ${config.dashboard.port}`);
177
+ }
178
+
179
+ // 13. Event listeners (synapse wiring)
180
+ this.setupEventListeners(synapseManager);
181
+
182
+ // 14. PID file
183
+ const pidPath = path.join(path.dirname(config.dbPath), 'marketing-brain.pid');
184
+ fs.writeFileSync(pidPath, String(process.pid));
185
+
186
+ // 15. Graceful shutdown
187
+ process.on('SIGINT', () => this.stop());
188
+ process.on('SIGTERM', () => this.stop());
189
+
190
+ // 16. Crash recovery — auto-restart on uncaught errors
191
+ process.on('uncaughtException', (err) => {
192
+ logger.error('Uncaught exception — restarting', { error: err.message, stack: err.stack });
193
+ this.logCrash('uncaughtException', err);
194
+ this.restart();
195
+ });
196
+ process.on('unhandledRejection', (reason) => {
197
+ logger.error('Unhandled rejection — restarting', { reason: String(reason) });
198
+ this.logCrash('unhandledRejection', reason instanceof Error ? reason : new Error(String(reason)));
199
+ this.restart();
200
+ });
201
+
202
+ logger.info(`Marketing Brain daemon started (PID: ${process.pid})`);
203
+ }
204
+
205
+ private logCrash(type: string, err: Error): void {
206
+ if (!this.config) return;
207
+ const crashLog = path.join(path.dirname(this.config.dbPath), 'crashes.log');
208
+ const entry = `[${new Date().toISOString()}] ${type}: ${err.message}\n${err.stack ?? ''}\n\n`;
209
+ try { fs.appendFileSync(crashLog, entry); } catch { /* best effort */ }
210
+ }
211
+
212
+ private cleanup(): void {
213
+ this.researchEngine?.stop();
214
+ this.learningEngine?.stop();
215
+ this.dashboardServer?.stop();
216
+ this.apiServer?.stop();
217
+ this.ipcServer?.stop();
218
+ this.db?.close();
219
+
220
+ this.db = null;
221
+ this.ipcServer = null;
222
+ this.apiServer = null;
223
+ this.dashboardServer = null;
224
+ this.learningEngine = null;
225
+ this.researchEngine = null;
226
+ }
227
+
228
+ restart(): void {
229
+ if (this.restarting) return;
230
+ this.restarting = true;
231
+
232
+ const logger = getLogger();
233
+ logger.info('Restarting Marketing Brain daemon...');
234
+
235
+ try { this.cleanup(); } catch { /* best effort cleanup */ }
236
+
237
+ this.restarting = false;
238
+ this.start(this.configPath);
239
+ }
240
+
241
+ stop(): void {
242
+ const logger = getLogger();
243
+ logger.info('Shutting down...');
244
+
245
+ this.cleanup();
246
+
247
+ if (this.config) {
248
+ const pidPath = path.join(path.dirname(this.config.dbPath), 'marketing-brain.pid');
249
+ try { fs.unlinkSync(pidPath); } catch { /* ignore */ }
250
+ }
251
+
252
+ logger.info('Marketing Brain daemon stopped');
253
+ process.exit(0);
254
+ }
255
+
256
+ private setupEventListeners(synapseManager: SynapseManager): void {
257
+ const bus = getEventBus();
258
+
259
+ bus.on('post:created', ({ postId, campaignId }) => {
260
+ if (campaignId) {
261
+ synapseManager.strengthen(
262
+ { type: 'post', id: postId },
263
+ { type: 'campaign', id: campaignId },
264
+ 'belongs_to',
265
+ );
266
+ }
267
+ });
268
+
269
+ bus.on('strategy:reported', ({ strategyId, postId }) => {
270
+ synapseManager.strengthen(
271
+ { type: 'strategy', id: strategyId },
272
+ { type: 'post', id: postId },
273
+ 'improves',
274
+ );
275
+ });
276
+
277
+ bus.on('rule:learned', ({ ruleId, pattern }) => {
278
+ getLogger().info(`New rule #${ruleId} learned: ${pattern}`);
279
+ });
280
+
281
+ bus.on('insight:created', ({ insightId, type }) => {
282
+ getLogger().info(`New insight #${insightId} (${type})`);
283
+ });
284
+ }
285
+ }
@@ -0,0 +1,72 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { spawn } from 'node:child_process';
4
+ import path from 'node:path';
5
+ import { IpcClient } from '../ipc/client.js';
6
+ import { getPipeName } from '../utils/paths.js';
7
+ import { registerTools } from './tools.js';
8
+
9
+ function spawnDaemon(): void {
10
+ const entryPoint = path.resolve(import.meta.dirname, '../index.ts');
11
+ const child = spawn(process.execPath, [
12
+ '--import', 'tsx',
13
+ entryPoint, 'daemon',
14
+ ], {
15
+ detached: true,
16
+ stdio: 'ignore',
17
+ cwd: path.resolve(import.meta.dirname, '../..'),
18
+ });
19
+ child.unref();
20
+ process.stderr.write(`Marketing Brain: Auto-started daemon (PID: ${child.pid})\n`);
21
+ }
22
+
23
+ async function connectWithRetry(ipc: IpcClient, retries: number, delayMs: number): Promise<void> {
24
+ for (let i = 0; i < retries; i++) {
25
+ try {
26
+ await ipc.connect();
27
+ return;
28
+ } catch {
29
+ if (i < retries - 1) {
30
+ await new Promise(r => setTimeout(r, delayMs));
31
+ }
32
+ }
33
+ }
34
+ throw new Error('Could not connect to daemon after retries');
35
+ }
36
+
37
+ export async function startMcpServer(): Promise<void> {
38
+ const server = new McpServer({
39
+ name: 'marketing-brain',
40
+ version: '0.1.0',
41
+ });
42
+
43
+ const ipc = new IpcClient(getPipeName());
44
+
45
+ try {
46
+ await ipc.connect();
47
+ } catch {
48
+ process.stderr.write('Marketing Brain: Daemon not running, starting automatically...\n');
49
+ spawnDaemon();
50
+ try {
51
+ await connectWithRetry(ipc, 10, 500);
52
+ } catch {
53
+ process.stderr.write('Marketing Brain: Could not connect to daemon after auto-start.\n');
54
+ process.exit(1);
55
+ }
56
+ }
57
+
58
+ registerTools(server, ipc);
59
+
60
+ const transport = new StdioServerTransport();
61
+ await server.connect(transport);
62
+
63
+ process.on('SIGINT', () => {
64
+ ipc.disconnect();
65
+ process.exit(0);
66
+ });
67
+
68
+ process.on('SIGTERM', () => {
69
+ ipc.disconnect();
70
+ process.exit(0);
71
+ });
72
+ }
@@ -0,0 +1,216 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { z } from 'zod';
3
+ import type { IpcClient } from '../ipc/client.js';
4
+
5
+ export function registerTools(server: McpServer, ipc: IpcClient): void {
6
+
7
+ // 1. post.draft — Check a post draft against rules
8
+ server.tool(
9
+ 'marketing_post_draft',
10
+ 'Check a post draft against learned marketing rules before publishing. Returns violations and recommendations.',
11
+ {
12
+ content: z.string().describe('The post content/text to check'),
13
+ platform: z.string().describe('Target platform (x, reddit, linkedin, bluesky)'),
14
+ },
15
+ async ({ content, platform }) => {
16
+ const result = await ipc.request('rule.check', { content, platform });
17
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
18
+ },
19
+ );
20
+
21
+ // 2. post.report — Report a published post
22
+ server.tool(
23
+ 'marketing_post_report',
24
+ 'Report a published post to track in the marketing brain. Stores content, platform, format, and creates synapse connections.',
25
+ {
26
+ platform: z.string().describe('Platform (x, reddit, linkedin, bluesky)'),
27
+ content: z.string().describe('Post content/text'),
28
+ format: z.string().optional().describe('Post format (text, image, video, thread, article)'),
29
+ url: z.string().optional().describe('Post URL'),
30
+ hashtags: z.string().optional().describe('Hashtags (comma-separated)'),
31
+ campaign: z.string().optional().describe('Campaign name (creates if not exists)'),
32
+ },
33
+ async ({ platform, content, format, url, hashtags, campaign }) => {
34
+ let campaignId: number | null = null;
35
+ if (campaign) {
36
+ const camp = await ipc.request('campaign.create', { name: campaign }) as { id: number };
37
+ campaignId = camp.id;
38
+ }
39
+ const result = await ipc.request('post.report', {
40
+ platform, content, format: format ?? 'text',
41
+ url, hashtags, campaign_id: campaignId,
42
+ status: 'published', published_at: new Date().toISOString(),
43
+ });
44
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
45
+ },
46
+ );
47
+
48
+ // 3. post.engagement — Update engagement metrics
49
+ server.tool(
50
+ 'marketing_post_engagement',
51
+ 'Update engagement metrics for a tracked post. Provide post ID and current metrics.',
52
+ {
53
+ post_id: z.number().describe('Post ID'),
54
+ likes: z.number().optional().describe('Current likes count'),
55
+ comments: z.number().optional().describe('Current comments count'),
56
+ shares: z.number().optional().describe('Current shares/retweets count'),
57
+ impressions: z.number().optional().describe('Current impressions count'),
58
+ clicks: z.number().optional().describe('Current clicks count'),
59
+ saves: z.number().optional().describe('Current saves/bookmarks count'),
60
+ reach: z.number().optional().describe('Current reach count'),
61
+ },
62
+ async (params) => {
63
+ const result = await ipc.request('post.engagement', params);
64
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
65
+ },
66
+ );
67
+
68
+ // 4. post.similar — Find similar posts
69
+ server.tool(
70
+ 'marketing_post_similar',
71
+ 'Find posts similar to a given post using synapse network spreading activation.',
72
+ {
73
+ post_id: z.number().describe('Post ID to find similar posts for'),
74
+ },
75
+ async ({ post_id }) => {
76
+ const result = await ipc.request('post.similar', { id: post_id });
77
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
78
+ },
79
+ );
80
+
81
+ // 5. campaign.create — Create a new campaign
82
+ server.tool(
83
+ 'marketing_campaign_create',
84
+ 'Create a new marketing campaign to group and track related posts.',
85
+ {
86
+ name: z.string().describe('Campaign name'),
87
+ brand: z.string().optional().describe('Brand name (e.g., REPOSIGNAL, Brain)'),
88
+ goal: z.string().optional().describe('Campaign goal'),
89
+ platform: z.string().optional().describe('Primary platform'),
90
+ },
91
+ async (params) => {
92
+ const result = await ipc.request('campaign.create', params);
93
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
94
+ },
95
+ );
96
+
97
+ // 6. campaign.stats — Campaign performance
98
+ server.tool(
99
+ 'marketing_campaign_stats',
100
+ 'Get performance statistics for a campaign including total engagement and post count.',
101
+ {
102
+ campaign_id: z.number().describe('Campaign ID'),
103
+ },
104
+ async ({ campaign_id }) => {
105
+ const result = await ipc.request('campaign.stats', { id: campaign_id });
106
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
107
+ },
108
+ );
109
+
110
+ // 7. strategy.report — Report a successful strategy
111
+ server.tool(
112
+ 'marketing_strategy_report',
113
+ 'Report a marketing strategy that worked. The brain will learn from it and suggest similar strategies later.',
114
+ {
115
+ description: z.string().describe('Strategy description'),
116
+ approach: z.string().optional().describe('Approach taken'),
117
+ outcome: z.string().optional().describe('Outcome/result'),
118
+ post_id: z.number().optional().describe('Related post ID'),
119
+ },
120
+ async (params) => {
121
+ const result = await ipc.request('strategy.report', params);
122
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
123
+ },
124
+ );
125
+
126
+ // 8. strategy.suggest — Get strategy suggestions
127
+ server.tool(
128
+ 'marketing_strategy_suggest',
129
+ 'Get strategy suggestions based on a topic or context. Searches past successful strategies.',
130
+ {
131
+ query: z.string().describe('Topic or context to search for'),
132
+ limit: z.number().optional().describe('Max results (default: 5)'),
133
+ },
134
+ async ({ query, limit }) => {
135
+ const result = await ipc.request('strategy.suggest', { query, limit: limit ?? 5 });
136
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
137
+ },
138
+ );
139
+
140
+ // 9. template.find — Find content templates
141
+ server.tool(
142
+ 'marketing_template_find',
143
+ 'Find reusable content templates that match a query or platform.',
144
+ {
145
+ query: z.string().optional().describe('Search query'),
146
+ platform: z.string().optional().describe('Filter by platform'),
147
+ limit: z.number().optional().describe('Max results'),
148
+ },
149
+ async ({ query, platform, limit }) => {
150
+ let result;
151
+ if (platform) {
152
+ result = await ipc.request('template.byPlatform', { platform, limit: limit ?? 10 });
153
+ } else {
154
+ result = await ipc.request('template.find', { query: query ?? '', limit: limit ?? 10 });
155
+ }
156
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
157
+ },
158
+ );
159
+
160
+ // 10. rule.check — Check draft against rules
161
+ server.tool(
162
+ 'marketing_rule_check',
163
+ 'Check a post draft against all learned marketing rules. Returns violations and recommendations.',
164
+ {
165
+ content: z.string().describe('Post content to check'),
166
+ platform: z.string().describe('Target platform'),
167
+ },
168
+ async ({ content, platform }) => {
169
+ const result = await ipc.request('rule.check', { content, platform });
170
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
171
+ },
172
+ );
173
+
174
+ // 11. insight.list — List current insights
175
+ server.tool(
176
+ 'marketing_insight_list',
177
+ 'Get current active marketing insights (trends, gaps, synergies, optimizations).',
178
+ {
179
+ type: z.string().optional().describe('Filter by type: trend, gap, synergy, template, optimization'),
180
+ limit: z.number().optional().describe('Max results'),
181
+ },
182
+ async ({ type, limit }) => {
183
+ let result;
184
+ if (type) {
185
+ result = await ipc.request('insight.byType', { type, limit: limit ?? 20 });
186
+ } else {
187
+ result = await ipc.request('insight.list', { limit: limit ?? 20 });
188
+ }
189
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
190
+ },
191
+ );
192
+
193
+ // 12. analytics.summary — Overall analytics
194
+ server.tool(
195
+ 'marketing_analytics_summary',
196
+ 'Get a complete analytics summary: posts, campaigns, strategies, rules, insights, and network stats.',
197
+ {},
198
+ async () => {
199
+ const result = await ipc.request('analytics.summary', {});
200
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
201
+ },
202
+ );
203
+
204
+ // 13. analytics.bestOf — Top performing content
205
+ server.tool(
206
+ 'marketing_analytics_best',
207
+ 'Get top performing posts, best strategies, and platform-level engagement stats.',
208
+ {
209
+ limit: z.number().optional().describe('Max results per category'),
210
+ },
211
+ async ({ limit }) => {
212
+ const result = await ipc.request('analytics.top', { limit: limit ?? 10 });
213
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
214
+ },
215
+ );
216
+ }