heyhank 0.1.0 → 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.
- package/LICENSE +21 -0
- package/README.md +83 -10
- package/bin/cli.ts +7 -7
- package/bin/ctl.ts +42 -42
- package/dist/assets/{AgentsPage-BPhirnCe.js → AgentsPage-B-AAmsMK.js} +3 -3
- package/dist/assets/AssistantPage-BV1Mfwdt.js +2 -0
- package/dist/assets/BusinessPage-tLpNEz19.js +1 -0
- package/dist/assets/{CronManager-DDbz-yiT.js → CronManager-B-K_n3Jg.js} +1 -1
- package/dist/assets/HelpPage-Bhf_j6Xr.js +1 -0
- package/dist/assets/{IntegrationsPage-CrOitCmJ.js → IntegrationsPage-DAMjs9tM.js} +1 -1
- package/dist/assets/JarvisHUD-C_TGXCCn.js +120 -0
- package/dist/assets/MediaPage-C48HTTrt.js +1 -0
- package/dist/assets/MemoryPage-JkC-qtgp.js +1 -0
- package/dist/assets/{PlatformDashboard-Do6F0O2p.js → PlatformDashboard-AUo7tNnE.js} +1 -1
- package/dist/assets/{Playground-Fc5cdc5p.js → Playground-AzNMsRBL.js} +1 -1
- package/dist/assets/{ProcessPanel-CslEiZkI.js → ProcessPanel-DpE_2sX3.js} +1 -1
- package/dist/assets/{PromptsPage-D2EhsdNO.js → PromptsPage-C2RQOs6p.js} +2 -2
- package/dist/assets/RunsPage-B9UOyO79.js +1 -0
- package/dist/assets/{SandboxManager-a1AVI5q2.js → SandboxManager-jHvYjwfh.js} +1 -1
- package/dist/assets/SettingsPage-BBJax6gt.js +51 -0
- package/dist/assets/SkillsMarketplace-IjmjfdjD.js +1 -0
- package/dist/assets/SocialMediaPage-DoPZHhr2.js +10 -0
- package/dist/assets/{TailscalePage-CHiFhZXF.js → TailscalePage-DDEY7ckO.js} +1 -1
- package/dist/assets/TelephonyPage-OPNBZYKt.js +9 -0
- package/dist/assets/{TerminalPage-Drwyrnfd.js → TerminalPage-BjMbHHW3.js} +1 -1
- package/dist/assets/{gemini-live-client-C7rqAW7G.js → gemini-live-client-C70FEtX2.js} +11 -8
- package/dist/assets/{index-CEqZnThB.js → index-BgYM4wXw.js} +94 -93
- package/dist/assets/index-BkjSoVgn.css +32 -0
- package/dist/assets/sw-register-C7NOHtIu.js +1 -0
- package/dist/assets/text-chat-client-BSbLJerZ.js +2 -0
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/package.json +6 -1
- package/server/agent-executor.ts +37 -2
- package/server/agent-store.ts +3 -3
- package/server/agent-types.ts +11 -0
- package/server/assistant-store.ts +232 -6
- package/server/auth-manager.ts +9 -0
- package/server/cache-headers.ts +1 -1
- package/server/calendar-service.ts +10 -0
- package/server/ceo/document-store.ts +129 -0
- package/server/ceo/finance-store.ts +343 -0
- package/server/ceo/kpi-store.ts +208 -0
- package/server/ceo/memory-import.ts +277 -0
- package/server/ceo/news-store.ts +208 -0
- package/server/ceo/template-store.ts +134 -0
- package/server/ceo/time-tracking-store.ts +227 -0
- package/server/claude-auth-monitor.ts +128 -0
- package/server/claude-code-worker.ts +86 -0
- package/server/claude-session-discovery.ts +74 -1
- package/server/cli-launcher.ts +32 -10
- package/server/codex-adapter.ts +2 -2
- package/server/codex-ws-proxy.cjs +1 -1
- package/server/container-manager.ts +4 -4
- package/server/content-intelligence/content-engine.ts +1112 -0
- package/server/content-intelligence/platform-knowledge.ts +870 -0
- package/server/cron-store.ts +3 -3
- package/server/embedding-service.ts +49 -0
- package/server/event-bus-types.ts +13 -0
- package/server/federation/node-store.ts +5 -4
- package/server/fs-utils.ts +28 -1
- package/server/hank-notifications-store.ts +91 -0
- package/server/hank-tool-executor.ts +1835 -0
- package/server/hank-tools.ts +2107 -0
- package/server/image-pull-manager.ts +2 -2
- package/server/index.ts +25 -2
- package/server/llm-providers-streaming.ts +541 -0
- package/server/llm-providers.ts +12 -0
- package/server/marketplace.ts +249 -0
- package/server/mcp-registry.ts +158 -0
- package/server/memory-service.ts +296 -0
- package/server/obsidian-sync.ts +184 -0
- package/server/provider-manager.ts +5 -2
- package/server/provider-registry.ts +12 -0
- package/server/reminder-scheduler.ts +37 -1
- package/server/routes/agent-routes.ts +2 -1
- package/server/routes/assistant-routes.ts +198 -5
- package/server/routes/ceo-finance-kpi-routes.ts +167 -0
- package/server/routes/ceo-news-time-routes.ts +137 -0
- package/server/routes/ceo-routes.ts +99 -0
- package/server/routes/content-routes.ts +116 -0
- package/server/routes/email-routes.ts +147 -0
- package/server/routes/env-routes.ts +3 -3
- package/server/routes/fs-routes.ts +12 -9
- package/server/routes/hank-chat-routes.ts +592 -0
- package/server/routes/llm-routes.ts +12 -0
- package/server/routes/marketplace-routes.ts +63 -0
- package/server/routes/media-routes.ts +1 -1
- package/server/routes/memory-routes.ts +127 -0
- package/server/routes/platform-routes.ts +14 -675
- package/server/routes/sandbox-routes.ts +1 -1
- package/server/routes/settings-routes.ts +51 -1
- package/server/routes/socialmedia-routes.ts +152 -2
- package/server/routes/system-routes.ts +2 -2
- package/server/routes/team-routes.ts +71 -0
- package/server/routes/telephony-routes.ts +98 -18
- package/server/routes.ts +36 -9
- package/server/session-creation-service.ts +2 -2
- package/server/session-orchestrator.ts +54 -2
- package/server/session-types.ts +2 -0
- package/server/settings-manager.ts +50 -2
- package/server/skill-discovery.ts +68 -0
- package/server/socialmedia/adapters/browser-adapter.ts +179 -0
- package/server/socialmedia/adapters/postiz-adapter.ts +291 -14
- package/server/socialmedia/manager.ts +234 -15
- package/server/socialmedia/store.ts +51 -1
- package/server/socialmedia/types.ts +35 -2
- package/server/socialview/browser-manager.ts +150 -0
- package/server/socialview/extractors.ts +1298 -0
- package/server/socialview/image-describe.ts +188 -0
- package/server/socialview/library.ts +119 -0
- package/server/socialview/poster.ts +276 -0
- package/server/socialview/routes.ts +371 -0
- package/server/socialview/style-analyzer.ts +187 -0
- package/server/socialview/style-profiles.ts +67 -0
- package/server/socialview/types.ts +166 -0
- package/server/socialview/vision.ts +127 -0
- package/server/socialview/vnc-manager.ts +110 -0
- package/server/style-injector.ts +135 -0
- package/server/team-service.ts +239 -0
- package/server/team-store.ts +75 -0
- package/server/team-types.ts +52 -0
- package/server/telephony/audio-bridge.ts +281 -35
- package/server/telephony/audio-recorder.ts +132 -0
- package/server/telephony/call-manager.ts +803 -104
- package/server/telephony/call-types.ts +67 -1
- package/server/telephony/esl-client.ts +319 -0
- package/server/telephony/freeswitch-sync.ts +155 -0
- package/server/telephony/phone-utils.ts +63 -0
- package/server/telephony/telephony-store.ts +9 -8
- package/server/url-validator.ts +82 -0
- package/server/vault-markdown.ts +317 -0
- package/server/vault-migration.ts +121 -0
- package/server/vault-store.ts +466 -0
- package/server/vault-watcher.ts +59 -0
- package/server/vector-store.ts +210 -0
- package/server/voice-pipeline/gemini-live-adapter.ts +97 -0
- package/server/voice-pipeline/greeting-cache.ts +200 -0
- package/server/voice-pipeline/manager.ts +249 -0
- package/server/voice-pipeline/pipeline.ts +335 -0
- package/server/voice-pipeline/providers/index.ts +47 -0
- package/server/voice-pipeline/providers/llm-internal.ts +527 -0
- package/server/voice-pipeline/providers/stt-google.ts +157 -0
- package/server/voice-pipeline/providers/tts-google.ts +126 -0
- package/server/voice-pipeline/types.ts +247 -0
- package/server/ws-bridge-types.ts +6 -1
- package/dist/assets/AssistantPage-DJ-cMQfb.js +0 -1
- package/dist/assets/HelpPage-DMfkzERp.js +0 -1
- package/dist/assets/MediaPage-CE5rdvkC.js +0 -1
- package/dist/assets/RunsPage-C5BZF5Rx.js +0 -1
- package/dist/assets/SettingsPage-DirhjQrJ.js +0 -51
- package/dist/assets/SocialMediaPage-DBuM28vD.js +0 -1
- package/dist/assets/TelephonyPage-x0VV0fOo.js +0 -1
- package/dist/assets/index-C8M_PUmX.css +0 -32
- package/dist/assets/sw-register-LSSpj6RU.js +0 -1
- package/server/socialmedia/adapters/ayrshare-adapter.ts +0 -169
|
@@ -0,0 +1,870 @@
|
|
|
1
|
+
// ─── Platform Knowledge Base ─────────────────────────────────────────────────
|
|
2
|
+
// Comprehensive, research-backed best practices for all major social media
|
|
3
|
+
// platforms. Used by the Content Engine to generate platform-optimized content.
|
|
4
|
+
// Data sourced from Hootsuite, Sprout Social, Buffer, Socialinsider, Later,
|
|
5
|
+
// vidIQ, TubeBuddy, and others — April 2026.
|
|
6
|
+
|
|
7
|
+
export interface PlatformSpec {
|
|
8
|
+
name: string;
|
|
9
|
+
key: string;
|
|
10
|
+
|
|
11
|
+
// Demographics
|
|
12
|
+
demographics: {
|
|
13
|
+
monthlyActiveUsers: string;
|
|
14
|
+
largestAgeGroup: string;
|
|
15
|
+
genderSplit: string;
|
|
16
|
+
coreAudience: string;
|
|
17
|
+
avgTimeSpent: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Posting schedule
|
|
21
|
+
bestTimes: {
|
|
22
|
+
bestDays: string[];
|
|
23
|
+
bestHours: string;
|
|
24
|
+
worstTimes: string;
|
|
25
|
+
timezone: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Frequency
|
|
29
|
+
frequency: {
|
|
30
|
+
recommended: string;
|
|
31
|
+
minimum: string;
|
|
32
|
+
maximum: string;
|
|
33
|
+
notes: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Content formats ranked by performance
|
|
37
|
+
formats: Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
engagementRate: string;
|
|
40
|
+
reachMultiplier: string;
|
|
41
|
+
bestFor: string;
|
|
42
|
+
}>;
|
|
43
|
+
|
|
44
|
+
// Recommended content mix
|
|
45
|
+
contentMix: Array<{
|
|
46
|
+
format: string;
|
|
47
|
+
percentage: string;
|
|
48
|
+
}>;
|
|
49
|
+
|
|
50
|
+
// Post specs
|
|
51
|
+
postSpecs: {
|
|
52
|
+
idealLength: string;
|
|
53
|
+
maxLength: string;
|
|
54
|
+
truncationPoint: string;
|
|
55
|
+
videoMaxLength: string;
|
|
56
|
+
videoIdealLength: string;
|
|
57
|
+
videoAspectRatio: string;
|
|
58
|
+
videoResolution: string;
|
|
59
|
+
imageAspectRatio: string;
|
|
60
|
+
imageResolution: string;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Algorithm
|
|
64
|
+
algorithm: {
|
|
65
|
+
topSignals: string[];
|
|
66
|
+
penalizedActions: string[];
|
|
67
|
+
keyInsight: string;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Hashtags
|
|
71
|
+
hashtags: {
|
|
72
|
+
optimal: string;
|
|
73
|
+
placement: string;
|
|
74
|
+
notes: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Hook strategy
|
|
78
|
+
hooks: {
|
|
79
|
+
timeToHook: string;
|
|
80
|
+
bestFormulas: string[];
|
|
81
|
+
avoid: string[];
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Platform-specific features that boost reach
|
|
85
|
+
boostFeatures: string[];
|
|
86
|
+
|
|
87
|
+
// Common mistakes
|
|
88
|
+
mistakes: string[];
|
|
89
|
+
|
|
90
|
+
// Ad specs
|
|
91
|
+
adSpecs: {
|
|
92
|
+
bestFormat: string;
|
|
93
|
+
bestAspectRatio: string;
|
|
94
|
+
bestResolution: string;
|
|
95
|
+
headlineLength: string;
|
|
96
|
+
bodyLength: string;
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── Facebook ────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
export const FACEBOOK: PlatformSpec = {
|
|
103
|
+
name: "Facebook",
|
|
104
|
+
key: "facebook",
|
|
105
|
+
|
|
106
|
+
demographics: {
|
|
107
|
+
monthlyActiveUsers: "3.07 billion",
|
|
108
|
+
largestAgeGroup: "25-34 (24.2%)",
|
|
109
|
+
genderSplit: "55.3% male, 44.7% female",
|
|
110
|
+
coreAudience: "Millennials (25-34) and Gen X (35-54)",
|
|
111
|
+
avgTimeSpent: "30-33 min/day",
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
bestTimes: {
|
|
115
|
+
bestDays: ["Tuesday", "Wednesday"],
|
|
116
|
+
bestHours: "12:00-8:00 PM local time",
|
|
117
|
+
worstTimes: "4:00-7:00 AM across all days",
|
|
118
|
+
timezone: "Audience local time",
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
frequency: {
|
|
122
|
+
recommended: "3-5 feed posts/week + 2-4 Reels + daily Stories",
|
|
123
|
+
minimum: "3 posts/week",
|
|
124
|
+
maximum: "2 posts/day (over-posting causes scroll-past)",
|
|
125
|
+
notes: "Consistency beats volume; algorithm rewards predictable rhythm",
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
formats: [
|
|
129
|
+
{ name: "Images", engagementRate: "5.20%", reachMultiplier: "1x (baseline)", bestFor: "Quick engagement" },
|
|
130
|
+
{ name: "Video/Reels", engagementRate: "4.84%", reachMultiplier: "3-5x reach", bestFor: "Discovery, new audience" },
|
|
131
|
+
{ name: "Carousels", engagementRate: "Higher than images", reachMultiplier: "1.5x", bestFor: "Swipe interaction, stories" },
|
|
132
|
+
{ name: "Text posts", engagementRate: "4.76%", reachMultiplier: "0.5x", bestFor: "Community discussion" },
|
|
133
|
+
{ name: "Link posts", engagementRate: "4.43%", reachMultiplier: "0.3x", bestFor: "Traffic (but penalized)" },
|
|
134
|
+
],
|
|
135
|
+
|
|
136
|
+
contentMix: [
|
|
137
|
+
{ format: "Reels/short video", percentage: "50-60%" },
|
|
138
|
+
{ format: "Native images/carousels", percentage: "20-25%" },
|
|
139
|
+
{ format: "Facebook Live", percentage: "10-15%" },
|
|
140
|
+
{ format: "Polls/questions/UGC", percentage: "10%" },
|
|
141
|
+
],
|
|
142
|
+
|
|
143
|
+
postSpecs: {
|
|
144
|
+
idealLength: "40-80 characters (66% more engagement)",
|
|
145
|
+
maxLength: "63,206 characters",
|
|
146
|
+
truncationPoint: "125 chars mobile, 477 chars desktop",
|
|
147
|
+
videoMaxLength: "90 seconds (Reels)",
|
|
148
|
+
videoIdealLength: "15-60 seconds",
|
|
149
|
+
videoAspectRatio: "9:16 vertical (Reels), 4:5 (feed)",
|
|
150
|
+
videoResolution: "1080x1920 (Reels), 1080x1350 (feed)",
|
|
151
|
+
imageAspectRatio: "1:1 or 4:5",
|
|
152
|
+
imageResolution: "1080x1080 or 1080x1350",
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
algorithm: {
|
|
156
|
+
topSignals: [
|
|
157
|
+
"Saves and shares (most powerful)",
|
|
158
|
+
"Substantive comments and conversations",
|
|
159
|
+
"Content originality",
|
|
160
|
+
"Recency (50% more same-day Reels surfaced)",
|
|
161
|
+
"User relationship history",
|
|
162
|
+
"Engagement velocity (first 30-60 min)",
|
|
163
|
+
],
|
|
164
|
+
penalizedActions: [
|
|
165
|
+
"Engagement bait ('Like if you agree') — 50-90% reach reduction",
|
|
166
|
+
"Flagged trigger words — up to 80% reach reduction",
|
|
167
|
+
"Clickbait headlines",
|
|
168
|
+
"Reposted content/TikTok watermarks",
|
|
169
|
+
"Excessive external links",
|
|
170
|
+
"Recycled low-quality content",
|
|
171
|
+
],
|
|
172
|
+
keyInsight: "All videos are now auto-classified as Reels. Saves > Shares > Comments > Likes in signal weight.",
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
hashtags: {
|
|
176
|
+
optimal: "1-2 per post",
|
|
177
|
+
placement: "Integrated naturally in the sentence",
|
|
178
|
+
notes: "More than 3 hashtags REDUCES engagement. Facebook doesn't rely on hashtags for discovery.",
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
hooks: {
|
|
182
|
+
timeToHook: "First line (125 chars on mobile before truncation)",
|
|
183
|
+
bestFormulas: [
|
|
184
|
+
"Open-ended questions inviting personal experience",
|
|
185
|
+
"'Save this for later' as soft CTA",
|
|
186
|
+
"Behind-the-scenes and authentic moments",
|
|
187
|
+
"This-or-that comparisons with images",
|
|
188
|
+
],
|
|
189
|
+
avoid: [
|
|
190
|
+
"Generic CTAs like 'Like and share!'",
|
|
191
|
+
"Only promotional content (use 80/20 rule: 80% value, 20% promo)",
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
boostFeatures: [
|
|
196
|
+
"Facebook Groups (massive reach advantage, high-trust spaces)",
|
|
197
|
+
"Facebook Live (significantly more engagement than pre-recorded)",
|
|
198
|
+
"Facebook Events (bypass algorithm via notifications)",
|
|
199
|
+
"Reels (3-5x reach of standard posts)",
|
|
200
|
+
],
|
|
201
|
+
|
|
202
|
+
mistakes: [
|
|
203
|
+
"Posting only promotional content",
|
|
204
|
+
"Using engagement bait phrases",
|
|
205
|
+
"Ignoring comments in the first hour",
|
|
206
|
+
"Posting content with TikTok watermarks",
|
|
207
|
+
"Over-relying on link posts",
|
|
208
|
+
],
|
|
209
|
+
|
|
210
|
+
adSpecs: {
|
|
211
|
+
bestFormat: "Video ads (feed) or Reels ads",
|
|
212
|
+
bestAspectRatio: "4:5 (feed), 9:16 (Reels)",
|
|
213
|
+
bestResolution: "1080x1350 (feed), 1080x1920 (Reels)",
|
|
214
|
+
headlineLength: "~5 words",
|
|
215
|
+
bodyLength: "~19 words",
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// ─── Instagram ───────────────────────────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
export const INSTAGRAM: PlatformSpec = {
|
|
222
|
+
name: "Instagram",
|
|
223
|
+
key: "instagram",
|
|
224
|
+
|
|
225
|
+
demographics: {
|
|
226
|
+
monthlyActiveUsers: "2+ billion",
|
|
227
|
+
largestAgeGroup: "18-24 (31.7%) and 25-34 (30.6%)",
|
|
228
|
+
genderSplit: "50.6% male, 49.4% female (global); 55.4% female in US",
|
|
229
|
+
coreAudience: "Gen Z and Millennials (84% under 45)",
|
|
230
|
+
avgTimeSpent: "33.9 min/day (53 min for 18-24)",
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
bestTimes: {
|
|
234
|
+
bestDays: ["Tuesday", "Wednesday", "Thursday"],
|
|
235
|
+
bestHours: "7-9 AM, 10 AM-2 PM, 5-9 PM local time",
|
|
236
|
+
worstTimes: "Friday-Sunday (lowest engagement)",
|
|
237
|
+
timezone: "Audience local time",
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
frequency: {
|
|
241
|
+
recommended: "3-5 feed posts/week + 2-4 Reels + daily Stories (3-7/day)",
|
|
242
|
+
minimum: "3 posts/week",
|
|
243
|
+
maximum: "7 posts/week (diminishing returns beyond)",
|
|
244
|
+
notes: "Doubling from 1-2 to 3-5 posts/week can more than double follower growth",
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
formats: [
|
|
248
|
+
{ name: "Carousels", engagementRate: "0.55% (highest)", reachMultiplier: "2x saves, 3x profile visits", bestFor: "Saves, loyalty, conversions" },
|
|
249
|
+
{ name: "Reels", engagementRate: "0.52%", reachMultiplier: "3-5x more reach", bestFor: "Discovery, new followers" },
|
|
250
|
+
{ name: "Mixed-media Carousels", engagementRate: "2.33%", reachMultiplier: "Very high", bestFor: "Only 7% use this — massive opportunity" },
|
|
251
|
+
{ name: "Static Images", engagementRate: "Declining (-17% YoY)", reachMultiplier: "Lowest", bestFor: "Aesthetic/culture posts only" },
|
|
252
|
+
],
|
|
253
|
+
|
|
254
|
+
contentMix: [
|
|
255
|
+
{ format: "Reels", percentage: "60-70%" },
|
|
256
|
+
{ format: "Carousels", percentage: "20-30%" },
|
|
257
|
+
{ format: "Single Images", percentage: "10%" },
|
|
258
|
+
],
|
|
259
|
+
|
|
260
|
+
postSpecs: {
|
|
261
|
+
idealLength: "1-150 chars (short), 700-2200 chars (educational/saves)",
|
|
262
|
+
maxLength: "2,200 characters",
|
|
263
|
+
truncationPoint: "125 characters before 'more' on mobile",
|
|
264
|
+
videoMaxLength: "3 minutes (Reels)",
|
|
265
|
+
videoIdealLength: "7-15 seconds (virality), up to 90s (tutorials)",
|
|
266
|
+
videoAspectRatio: "9:16 vertical",
|
|
267
|
+
videoResolution: "1080x1920",
|
|
268
|
+
imageAspectRatio: "4:5 portrait (recommended)",
|
|
269
|
+
imageResolution: "1080x1350",
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
algorithm: {
|
|
273
|
+
topSignals: [
|
|
274
|
+
"DM sends/shares (#1 signal, especially for Reels)",
|
|
275
|
+
"Saves (strong indicator of high-value content)",
|
|
276
|
+
"Watch time / dwell time",
|
|
277
|
+
"Shares (public reshares to Stories/feed)",
|
|
278
|
+
"Meaningful comments (depth matters, not 'nice!')",
|
|
279
|
+
"Likes (weakest signal)",
|
|
280
|
+
],
|
|
281
|
+
penalizedActions: [
|
|
282
|
+
"Reposting TikTok content with watermarks (explicitly de-ranked)",
|
|
283
|
+
"Using banned hashtags (even one can make posts invisible)",
|
|
284
|
+
"Bots/automation tools (mass-following, auto-liking)",
|
|
285
|
+
"Buying followers/likes",
|
|
286
|
+
"Rapid unnatural activity (>40 comments/hour triggers bot detection)",
|
|
287
|
+
"Repetitive identical hashtags",
|
|
288
|
+
],
|
|
289
|
+
keyInsight: "Keyword-rich captions generate ~30% more reach and 2x more likes than hashtag-heavy posts. Write captions for SEO.",
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
hashtags: {
|
|
293
|
+
optimal: "3-5 for Reels, 5-7 for feed posts",
|
|
294
|
+
placement: "In caption (36% more reach than comment placement)",
|
|
295
|
+
notes: "Keyword-rich captions > hashtags for discovery. Instagram confirmed zero algorithmic difference between caption and comment, but caption gets indexed immediately.",
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
hooks: {
|
|
299
|
+
timeToHook: "First 3 seconds for Reels (50% drop-off), first 125 chars for captions",
|
|
300
|
+
bestFormulas: [
|
|
301
|
+
"Bold on-screen text in first frame",
|
|
302
|
+
"Curiosity questions",
|
|
303
|
+
"Show the payoff first, then explain",
|
|
304
|
+
"Pattern interrupts (unexpected visuals)",
|
|
305
|
+
],
|
|
306
|
+
avoid: [
|
|
307
|
+
"Slow intros or branding sequences",
|
|
308
|
+
"Generic motivational quotes without unique angle",
|
|
309
|
+
],
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
boostFeatures: [
|
|
313
|
+
"Collab posts (up to 5 collaborators, multiplies audience reach)",
|
|
314
|
+
"Mixed-media carousels (image + video, only 7% use this)",
|
|
315
|
+
"Interactive Stories (polls, quizzes, questions = 2x engagement)",
|
|
316
|
+
"Remix features (duet-style for Reels)",
|
|
317
|
+
"Product tagging (up to 30 products per Reel)",
|
|
318
|
+
],
|
|
319
|
+
|
|
320
|
+
mistakes: [
|
|
321
|
+
"Using only static images (declining -17% YoY)",
|
|
322
|
+
"Hashtag stuffing (30 hashtags strategy is dead)",
|
|
323
|
+
"Ignoring Reels (primary discovery engine)",
|
|
324
|
+
"Not using captions/subtitles on video",
|
|
325
|
+
"Posting TikTok watermarked content",
|
|
326
|
+
],
|
|
327
|
+
|
|
328
|
+
adSpecs: {
|
|
329
|
+
bestFormat: "Reels ads or Carousel ads",
|
|
330
|
+
bestAspectRatio: "9:16 (Reels), 1:1 (Carousel)",
|
|
331
|
+
bestResolution: "1080x1920 (Reels), 1080x1080 (Carousel)",
|
|
332
|
+
headlineLength: "~40 characters",
|
|
333
|
+
bodyLength: "~125 characters (before truncation)",
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// ─── TikTok ──────────────────────────────────────────────────────────────────
|
|
338
|
+
|
|
339
|
+
export const TIKTOK: PlatformSpec = {
|
|
340
|
+
name: "TikTok",
|
|
341
|
+
key: "tiktok",
|
|
342
|
+
|
|
343
|
+
demographics: {
|
|
344
|
+
monthlyActiveUsers: "2 billion",
|
|
345
|
+
largestAgeGroup: "18-24 (36%) and 25-34 (32%)",
|
|
346
|
+
genderSplit: "54% female, 46% male (global); 61% female in US",
|
|
347
|
+
coreAudience: "Gen Z and young Millennials (avg age 26.5)",
|
|
348
|
+
avgTimeSpent: "52 min/day (18-24), 34 min/day average",
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
bestTimes: {
|
|
352
|
+
bestDays: ["Tuesday", "Wednesday", "Thursday"],
|
|
353
|
+
bestHours: "6:00-11:00 PM weekdays; Sunday 9 AM is single best slot",
|
|
354
|
+
worstTimes: "Weekday afternoons 12-5 PM",
|
|
355
|
+
timezone: "Audience local time",
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
frequency: {
|
|
359
|
+
recommended: "2-5 posts/week",
|
|
360
|
+
minimum: "2 posts/week",
|
|
361
|
+
maximum: "5 posts/day (diminishing returns beyond)",
|
|
362
|
+
notes: "Median views stay flat (~500) regardless of frequency. More posts = more viral lottery tickets.",
|
|
363
|
+
},
|
|
364
|
+
|
|
365
|
+
formats: [
|
|
366
|
+
{ name: "Educational/Edutainment", engagementRate: "2.8x higher AVD", reachMultiplier: "High", bestFor: "Saves, completion rates, search" },
|
|
367
|
+
{ name: "Entertainment/humor", engagementRate: "High shares", reachMultiplier: "Highest virality", bestFor: "Widest reach" },
|
|
368
|
+
{ name: "Personal storytelling", engagementRate: "Strong comments", reachMultiplier: "Medium", bestFor: "Trust, loyalty" },
|
|
369
|
+
{ name: "Before/after transformations", engagementRate: "High completion", reachMultiplier: "High", bestFor: "Built-in visual hook" },
|
|
370
|
+
{ name: "Behind-the-scenes", engagementRate: "Medium", reachMultiplier: "Medium", bestFor: "Authenticity, lo-fi content" },
|
|
371
|
+
],
|
|
372
|
+
|
|
373
|
+
contentMix: [
|
|
374
|
+
{ format: "Community/value content (educational, entertaining)", percentage: "70%" },
|
|
375
|
+
{ format: "Interactive content (questions, duets, stitches)", percentage: "20%" },
|
|
376
|
+
{ format: "Brand storytelling (transparent, lo-fi)", percentage: "10%" },
|
|
377
|
+
],
|
|
378
|
+
|
|
379
|
+
postSpecs: {
|
|
380
|
+
idealLength: "Short, keyword-rich captions",
|
|
381
|
+
maxLength: "4,000 characters",
|
|
382
|
+
truncationPoint: "~150 characters before 'more'",
|
|
383
|
+
videoMaxLength: "10 minutes (some accounts: 30 min)",
|
|
384
|
+
videoIdealLength: "21-34 seconds (highest completion), 11-18s (virality)",
|
|
385
|
+
videoAspectRatio: "9:16 vertical",
|
|
386
|
+
videoResolution: "1080x1920",
|
|
387
|
+
imageAspectRatio: "9:16",
|
|
388
|
+
imageResolution: "1080x1920",
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
algorithm: {
|
|
392
|
+
topSignals: [
|
|
393
|
+
"Watch time and completion rate (40-50% of weight, threshold ~70%)",
|
|
394
|
+
"Saves and shares (weighted above likes)",
|
|
395
|
+
"Comment quality (depth matters more than count)",
|
|
396
|
+
"Rewatches (strong quality signal)",
|
|
397
|
+
"Production quality (lighting, audio, editing — NEW in 2026)",
|
|
398
|
+
"Follower-first testing (shown to followers before broader distribution)",
|
|
399
|
+
],
|
|
400
|
+
penalizedActions: [
|
|
401
|
+
"Posting bursts after inactivity (7-14 day shadowban)",
|
|
402
|
+
"10+ posts/day or mass liking/commenting",
|
|
403
|
+
"Low-quality AI-generated content",
|
|
404
|
+
"Copyright violations (especially business accounts)",
|
|
405
|
+
"Misleading hooks that don't match content",
|
|
406
|
+
"Banned/flagged hashtags",
|
|
407
|
+
],
|
|
408
|
+
keyInsight: "TikTok is now a full search engine. Keywords must appear in spoken audio, on-screen text, AND captions for maximum discoverability.",
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
hashtags: {
|
|
412
|
+
optimal: "3-5 per post",
|
|
413
|
+
placement: "In caption",
|
|
414
|
+
notes: "3-layer formula: 1 niche tag (<1M videos) + 2-3 thematic tags + 1 optional trending tag. #fyp still used but niche tags drive higher conversion.",
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
hooks: {
|
|
418
|
+
timeToHook: "Less than 3 seconds (50% of viewers decide to swipe)",
|
|
419
|
+
bestFormulas: [
|
|
420
|
+
"Dual-hook: text overlay + voiceover simultaneously",
|
|
421
|
+
"Curiosity gap: bold claim creating information gap",
|
|
422
|
+
"Pattern interrupt: unexpected visuals, jump cuts",
|
|
423
|
+
"Direct address: 'Stop scrolling if you...'",
|
|
424
|
+
],
|
|
425
|
+
avoid: [
|
|
426
|
+
"Misleading hooks that don't match content",
|
|
427
|
+
"Slow intros or branding sequences",
|
|
428
|
+
"Generic hooks without specific value proposition",
|
|
429
|
+
],
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
boostFeatures: [
|
|
433
|
+
"TikTok Shop (live shopping = 22% higher conversion)",
|
|
434
|
+
"Duets and Stitches (collaborative engagement)",
|
|
435
|
+
"Trending audio (use within first week of trend)",
|
|
436
|
+
"TikTok SEO (multi-layer keyword strategy)",
|
|
437
|
+
"Creator account for full music library access",
|
|
438
|
+
],
|
|
439
|
+
|
|
440
|
+
mistakes: [
|
|
441
|
+
"Using business account when trending sounds are critical",
|
|
442
|
+
"Posting overly polished/produced content (authenticity wins)",
|
|
443
|
+
"Ignoring TikTok search optimization",
|
|
444
|
+
"Not saying keywords in spoken audio",
|
|
445
|
+
"Sudden scaling from low to aggressive posting",
|
|
446
|
+
],
|
|
447
|
+
|
|
448
|
+
adSpecs: {
|
|
449
|
+
bestFormat: "In-feed video ads (native-looking)",
|
|
450
|
+
bestAspectRatio: "9:16",
|
|
451
|
+
bestResolution: "1080x1920",
|
|
452
|
+
headlineLength: "~12-30 characters",
|
|
453
|
+
bodyLength: "~100 characters",
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// ─── LinkedIn ────────────────────────────────────────────────────────────────
|
|
458
|
+
|
|
459
|
+
export const LINKEDIN: PlatformSpec = {
|
|
460
|
+
name: "LinkedIn",
|
|
461
|
+
key: "linkedin",
|
|
462
|
+
|
|
463
|
+
demographics: {
|
|
464
|
+
monthlyActiveUsers: "310 million active (1.3B total members)",
|
|
465
|
+
largestAgeGroup: "25-34 (33.4%)",
|
|
466
|
+
genderSplit: "56.8% male, 43.2% female",
|
|
467
|
+
coreAudience: "Professionals, B2B decision-makers, 54% US users earn >$100K",
|
|
468
|
+
avgTimeSpent: "~7-10 min/session",
|
|
469
|
+
},
|
|
470
|
+
|
|
471
|
+
bestTimes: {
|
|
472
|
+
bestDays: ["Tuesday", "Wednesday", "Thursday"],
|
|
473
|
+
bestHours: "10:00 AM - 2:00 PM local time",
|
|
474
|
+
worstTimes: "Saturday and Sunday (lowest engagement)",
|
|
475
|
+
timezone: "Target audience local time",
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
frequency: {
|
|
479
|
+
recommended: "2-5 posts/week",
|
|
480
|
+
minimum: "1 post/week",
|
|
481
|
+
maximum: "1 post/day (second post suppresses first)",
|
|
482
|
+
notes: "2-5 posts/week = +1,182 impressions per post vs 1/week. Consistency beats quantity.",
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
formats: [
|
|
486
|
+
{ name: "Carousel/Document (PDF)", engagementRate: "7.00-21.77% (highest)", reachMultiplier: "585% more than text", bestFor: "Dwell time, saves, authority" },
|
|
487
|
+
{ name: "Polls", engagementRate: "4.40%", reachMultiplier: "High", bestFor: "One-click engagement, market research" },
|
|
488
|
+
{ name: "Multi-image posts", engagementRate: "5-6%", reachMultiplier: "High", bestFor: "Likes, visual storytelling" },
|
|
489
|
+
{ name: "Video", engagementRate: "3.5-4%", reachMultiplier: "Medium", bestFor: "Vertical 9:16 with captions" },
|
|
490
|
+
{ name: "Text-only", engagementRate: "2-4%", reachMultiplier: "Medium", bestFor: "Personal stories, sharp writing" },
|
|
491
|
+
{ name: "External link posts", engagementRate: "1.5-2%", reachMultiplier: "40-60% reach PENALTY", bestFor: "Avoid — put link in first comment" },
|
|
492
|
+
],
|
|
493
|
+
|
|
494
|
+
contentMix: [
|
|
495
|
+
{ format: "Carousel/Document (PDF)", percentage: "40%" },
|
|
496
|
+
{ format: "Text + image posts", percentage: "25%" },
|
|
497
|
+
{ format: "Video (vertical, native)", percentage: "20%" },
|
|
498
|
+
{ format: "Polls and engagement posts", percentage: "15%" },
|
|
499
|
+
],
|
|
500
|
+
|
|
501
|
+
postSpecs: {
|
|
502
|
+
idealLength: "1,300-2,500 characters (200-400 words)",
|
|
503
|
+
maxLength: "3,000 characters",
|
|
504
|
+
truncationPoint: "140 chars mobile, 210 chars desktop (before 'see more')",
|
|
505
|
+
videoMaxLength: "15 min (desktop), 10 min (mobile)",
|
|
506
|
+
videoIdealLength: "30 seconds - 3 minutes",
|
|
507
|
+
videoAspectRatio: "9:16 vertical (preferred) or 1:1 square",
|
|
508
|
+
videoResolution: "1080p minimum",
|
|
509
|
+
imageAspectRatio: "1:1 or 4:5",
|
|
510
|
+
imageResolution: "1080x1080 or 1080x1350",
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
algorithm: {
|
|
514
|
+
topSignals: [
|
|
515
|
+
"Dwell time (#1 factor — carousels win here)",
|
|
516
|
+
"Relevance and topic match (LinkedIn builds your 'topic DNA')",
|
|
517
|
+
"Comment quality (10+ words from relevant people = 5-7x weight)",
|
|
518
|
+
"Saves/bookmarks (1 save = ~5x a like, ~2x a comment)",
|
|
519
|
+
"Expertise and authority (original insights, data, actionable advice)",
|
|
520
|
+
"Engagement velocity (first 60-90 min = 70% of total reach)",
|
|
521
|
+
],
|
|
522
|
+
penalizedActions: [
|
|
523
|
+
"External links in post body (40-60% reach reduction)",
|
|
524
|
+
"Engagement pods (shadow ban, 60-90 day recovery)",
|
|
525
|
+
"Engagement bait ('Comment YES to get...')",
|
|
526
|
+
"5+ hashtags (spam flag)",
|
|
527
|
+
"Posting more than 1x per day (cannibalization)",
|
|
528
|
+
"Editing post within first hour (resets distribution)",
|
|
529
|
+
"Tagging people who don't engage",
|
|
530
|
+
"High-volume cold DMs ('Volume Tax' penalty)",
|
|
531
|
+
"Generic/AI-generated comments (pattern detection)",
|
|
532
|
+
],
|
|
533
|
+
keyInsight: "Personal profiles get 561% more reach and 5-8x more engagement than company pages. LinkedIn is a personal-brand platform.",
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
hashtags: {
|
|
537
|
+
optimal: "3-5 per post",
|
|
538
|
+
placement: "End of post, never scattered in text",
|
|
539
|
+
notes: "LinkedIn removed hashtag following in 2024-2025. Hashtags now function as SEO keywords for LinkedIn search only.",
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
hooks: {
|
|
543
|
+
timeToHook: "First 140 characters (mobile truncation point)",
|
|
544
|
+
bestFormulas: [
|
|
545
|
+
"Contrarian statement: 'Most LinkedIn advice is making you invisible.'",
|
|
546
|
+
"Specific result + tease: 'I grew from 500 to 15K followers in 90 days.'",
|
|
547
|
+
"Challenge a belief: 'You don't need to post every day.'",
|
|
548
|
+
"Personal story opener: 'I got fired on a Tuesday.'",
|
|
549
|
+
"Question that creates tension",
|
|
550
|
+
],
|
|
551
|
+
avoid: [
|
|
552
|
+
"Corporate/polished headlines that feel like ad copy",
|
|
553
|
+
"Clickbait without payoff",
|
|
554
|
+
"Generic motivational openings",
|
|
555
|
+
],
|
|
556
|
+
},
|
|
557
|
+
|
|
558
|
+
boostFeatures: [
|
|
559
|
+
"Carousel/Document posts (PDF uploads — 21.77% engagement)",
|
|
560
|
+
"LinkedIn Newsletter (25-35% open rates, auto-invites new followers)",
|
|
561
|
+
"Strategic commenting on others' posts (5 thoughtful comments/day)",
|
|
562
|
+
"Employee advocacy (7x higher lead conversion via personal profiles)",
|
|
563
|
+
"Reply to all comments within 60 min (doubles engagement)",
|
|
564
|
+
],
|
|
565
|
+
|
|
566
|
+
mistakes: [
|
|
567
|
+
"Posting external links in post body (put in first comment)",
|
|
568
|
+
"Using company page instead of personal profile for organic reach",
|
|
569
|
+
"Posting more than once per day",
|
|
570
|
+
"Using engagement pods (coordinated activity detection is aggressive)",
|
|
571
|
+
"Editing posts within the first hour",
|
|
572
|
+
"Generic AI-generated comments",
|
|
573
|
+
],
|
|
574
|
+
|
|
575
|
+
adSpecs: {
|
|
576
|
+
bestFormat: "Carousel ads or Document ads",
|
|
577
|
+
bestAspectRatio: "1:1 (carousel) or 4:5 (single image)",
|
|
578
|
+
bestResolution: "1080x1080 or 1080x1350",
|
|
579
|
+
headlineLength: "~70 characters",
|
|
580
|
+
bodyLength: "~150 characters (before truncation)",
|
|
581
|
+
},
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
// ─── X (Twitter) ─────────────────────────────────────────────────────────────
|
|
585
|
+
|
|
586
|
+
export const X_TWITTER: PlatformSpec = {
|
|
587
|
+
name: "X (Twitter)",
|
|
588
|
+
key: "x",
|
|
589
|
+
|
|
590
|
+
demographics: {
|
|
591
|
+
monthlyActiveUsers: "557 million",
|
|
592
|
+
largestAgeGroup: "25-34 (37.5%) and 18-24 (32.1%)",
|
|
593
|
+
genderSplit: "63.7% male, 36.3% female",
|
|
594
|
+
coreAudience: "18-34 covers ~70% of user base. News/tech/politics oriented.",
|
|
595
|
+
avgTimeSpent: "28 min/day globally, 34 min/day in US",
|
|
596
|
+
},
|
|
597
|
+
|
|
598
|
+
bestTimes: {
|
|
599
|
+
bestDays: ["Tuesday", "Wednesday", "Thursday"],
|
|
600
|
+
bestHours: "9:00 AM - 2:00 PM local time",
|
|
601
|
+
worstTimes: "Saturday (worst day overall)",
|
|
602
|
+
timezone: "Audience local time",
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
frequency: {
|
|
606
|
+
recommended: "3-5 posts/day",
|
|
607
|
+
minimum: "1 post/day",
|
|
608
|
+
maximum: "15-30/day only if quality maintained",
|
|
609
|
+
notes: "Algorithm penalizes high volume with low per-post engagement. The 70/30 rule: spend 70% replying to others, 30% on original posts.",
|
|
610
|
+
},
|
|
611
|
+
|
|
612
|
+
formats: [
|
|
613
|
+
{ name: "Text", engagementRate: "3.56% (highest!)", reachMultiplier: "Baseline", bestFor: "X is the ONLY platform where text beats video" },
|
|
614
|
+
{ name: "Images", engagementRate: "3.40%", reachMultiplier: "~2x algorithmic boost", bestFor: "Visual complement to text" },
|
|
615
|
+
{ name: "Video", engagementRate: "2.96%", reachMultiplier: "~2x", bestFor: "Engagement, watch time" },
|
|
616
|
+
{ name: "Threads (8-12 tweets)", engagementRate: "63% more impressions", reachMultiplier: "High", bestFor: "Long-form, storytelling, authority" },
|
|
617
|
+
{ name: "Polls", engagementRate: "+21% vs average", reachMultiplier: "Medium", bestFor: "Lowest-friction engagement" },
|
|
618
|
+
{ name: "Link posts", engagementRate: "~0% (non-Premium!)", reachMultiplier: "Near zero", bestFor: "AVOID without Premium" },
|
|
619
|
+
],
|
|
620
|
+
|
|
621
|
+
contentMix: [
|
|
622
|
+
{ format: "Text + image posts", percentage: "40%" },
|
|
623
|
+
{ format: "Pure text", percentage: "25%" },
|
|
624
|
+
{ format: "Threads", percentage: "15%" },
|
|
625
|
+
{ format: "Video", percentage: "10%" },
|
|
626
|
+
{ format: "Polls and questions", percentage: "10%" },
|
|
627
|
+
],
|
|
628
|
+
|
|
629
|
+
postSpecs: {
|
|
630
|
+
idealLength: "71-100 characters (17% higher engagement)",
|
|
631
|
+
maxLength: "280 characters (free), 25,000 (Premium)",
|
|
632
|
+
truncationPoint: "280 characters (no truncation on free)",
|
|
633
|
+
videoMaxLength: "2:20 (free), 4 hours (Premium+)",
|
|
634
|
+
videoIdealLength: "Under 2 minutes 20 seconds",
|
|
635
|
+
videoAspectRatio: "9:16 vertical preferred, 16:9 or 1:1",
|
|
636
|
+
videoResolution: "1080x1920 (vertical), 1280x720 (landscape)",
|
|
637
|
+
imageAspectRatio: "16:9 or 1:1",
|
|
638
|
+
imageResolution: "1200x675 or 1080x1080",
|
|
639
|
+
},
|
|
640
|
+
|
|
641
|
+
algorithm: {
|
|
642
|
+
topSignals: [
|
|
643
|
+
"Reply-to-reply chain (75x weight of a like)",
|
|
644
|
+
"Conversation: reply + author reply back (150x!)",
|
|
645
|
+
"Reply (27x a like)",
|
|
646
|
+
"Repost/Retweet (20x a like)",
|
|
647
|
+
"Dwell time (time-on-post is #1 metric in 2026)",
|
|
648
|
+
"Engagement velocity (first 30 minutes critical)",
|
|
649
|
+
"TweepCred score (hidden reputation 0-100, below 65 = only 3 tweets distributed)",
|
|
650
|
+
"Premium status (4x in-network boost, 2x out-of-network)",
|
|
651
|
+
],
|
|
652
|
+
penalizedActions: [
|
|
653
|
+
"External links (non-Premium): near-zero engagement since March 2026",
|
|
654
|
+
"3+ hashtags per tweet",
|
|
655
|
+
"Starting tweets with hashtags",
|
|
656
|
+
"Follow/unfollow tactics (#1 shadowban trigger)",
|
|
657
|
+
"Engagement pods / auto-engagement",
|
|
658
|
+
"Mass unfollowing events (can trigger 3-month shadowban)",
|
|
659
|
+
">50 follows/day, >100 likes/hour, >50 retweets/hour, >30 replies/hour",
|
|
660
|
+
],
|
|
661
|
+
keyInsight: "X is pay-to-play. Premium gets 6-10x more reach. Free accounts get near-zero link engagement. Conversations (reply chains) are the most powerful signal (150x a like).",
|
|
662
|
+
},
|
|
663
|
+
|
|
664
|
+
hashtags: {
|
|
665
|
+
optimal: "1-2 per tweet",
|
|
666
|
+
placement: "Mid-tweet (never start with hashtag)",
|
|
667
|
+
notes: "1-2 hashtags = 21-55% more engagement. 3+ = looks like bot. AI understands content without hashtags now.",
|
|
668
|
+
},
|
|
669
|
+
|
|
670
|
+
hooks: {
|
|
671
|
+
timeToHook: "First line (entire tweet visible, no truncation at 280)",
|
|
672
|
+
bestFormulas: [
|
|
673
|
+
"Bold claim that challenges conventional wisdom",
|
|
674
|
+
"Specific number or result",
|
|
675
|
+
"Contrarian take on industry trend",
|
|
676
|
+
"Personal story with unexpected twist",
|
|
677
|
+
],
|
|
678
|
+
avoid: [
|
|
679
|
+
"Generic motivational content",
|
|
680
|
+
"Engagement bait",
|
|
681
|
+
],
|
|
682
|
+
},
|
|
683
|
+
|
|
684
|
+
boostFeatures: [
|
|
685
|
+
"X Premium (6-10x reach boost, mandatory for serious marketing)",
|
|
686
|
+
"Threads (8-12 tweets, 63% more impressions than single tweets)",
|
|
687
|
+
"Reply strategy (70% time replying, 30% original content)",
|
|
688
|
+
"X Communities (67% engagement increase)",
|
|
689
|
+
"X Spaces (live audio for authority building)",
|
|
690
|
+
],
|
|
691
|
+
|
|
692
|
+
mistakes: [
|
|
693
|
+
"Not having X Premium (visibility is directly tied to subscription)",
|
|
694
|
+
"Posting external links without Premium (0% engagement)",
|
|
695
|
+
"Using 3+ hashtags",
|
|
696
|
+
"Starting tweets with hashtags",
|
|
697
|
+
"Follow/unfollow tactics",
|
|
698
|
+
"Ignoring replies (conversations are 150x more valuable than likes)",
|
|
699
|
+
],
|
|
700
|
+
|
|
701
|
+
adSpecs: {
|
|
702
|
+
bestFormat: "Image or video ads",
|
|
703
|
+
bestAspectRatio: "16:9 (landscape) or 1:1 (square)",
|
|
704
|
+
bestResolution: "1200x675 or 1080x1080",
|
|
705
|
+
headlineLength: "~70 characters",
|
|
706
|
+
bodyLength: "~280 characters",
|
|
707
|
+
},
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// ─── YouTube ─────────────────────────────────────────────────────────────────
|
|
711
|
+
|
|
712
|
+
export const YOUTUBE: PlatformSpec = {
|
|
713
|
+
name: "YouTube",
|
|
714
|
+
key: "youtube",
|
|
715
|
+
|
|
716
|
+
demographics: {
|
|
717
|
+
monthlyActiveUsers: "2.85 billion",
|
|
718
|
+
largestAgeGroup: "15-34 (largest combined segment)",
|
|
719
|
+
genderSplit: "54.3% male, 45.7% female (global); 51.2% female in US",
|
|
720
|
+
coreAudience: "Broadest of all platforms. 55+ is fastest-growing segment.",
|
|
721
|
+
avgTimeSpent: "27 hours/month per user",
|
|
722
|
+
},
|
|
723
|
+
|
|
724
|
+
bestTimes: {
|
|
725
|
+
bestDays: ["Sunday", "Tuesday", "Monday"],
|
|
726
|
+
bestHours: "Long-form: 8-11 AM; Shorts: 6-9 PM",
|
|
727
|
+
worstTimes: "Upload 2-3 hours before audience peak for indexing",
|
|
728
|
+
timezone: "Target audience local time",
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
frequency: {
|
|
732
|
+
recommended: "1-2 long-form/week + 3-5 Shorts/week",
|
|
733
|
+
minimum: "1 long-form/week + 2-3 Shorts/week",
|
|
734
|
+
maximum: "3+/week long-form only if quality maintained",
|
|
735
|
+
notes: "Channels posting 12+/month grow views 8x faster. Quality > quantity always. Hybrid (Shorts + long-form) grows subs 3x faster.",
|
|
736
|
+
},
|
|
737
|
+
|
|
738
|
+
formats: [
|
|
739
|
+
{ name: "Shorts (50-60 sec)", engagementRate: "5.91%", reachMultiplier: "74% views from non-subscribers", bestFor: "Discovery, sub growth (29.2 subs per 10K views)" },
|
|
740
|
+
{ name: "Long-form (8-15 min)", engagementRate: "Lower but deeper", reachMultiplier: "5-10x higher RPM", bestFor: "Revenue, authority, SEO" },
|
|
741
|
+
{ name: "Community posts (polls)", engagementRate: "Highest in community", reachMultiplier: "Keeps algorithm presence", bestFor: "Between-upload visibility" },
|
|
742
|
+
],
|
|
743
|
+
|
|
744
|
+
contentMix: [
|
|
745
|
+
{ format: "Long-form videos (8-15 min)", percentage: "40%" },
|
|
746
|
+
{ format: "YouTube Shorts (50-60 sec)", percentage: "40%" },
|
|
747
|
+
{ format: "Community posts", percentage: "20%" },
|
|
748
|
+
],
|
|
749
|
+
|
|
750
|
+
postSpecs: {
|
|
751
|
+
idealLength: "Title: 60-70 characters, front-load keyword in first 50",
|
|
752
|
+
maxLength: "Title: 100 chars, Description: 5,000 chars",
|
|
753
|
+
truncationPoint: "Title truncates at ~60 chars on mobile",
|
|
754
|
+
videoMaxLength: "12 hours (or 256 GB)",
|
|
755
|
+
videoIdealLength: "Long-form: 8-15 min (8 min minimum for mid-roll ads). Shorts: 50-60 sec.",
|
|
756
|
+
videoAspectRatio: "16:9 (long-form), 9:16 (Shorts)",
|
|
757
|
+
videoResolution: "1920x1080 (long-form), 1080x1920 (Shorts)",
|
|
758
|
+
imageAspectRatio: "16:9 (thumbnails)",
|
|
759
|
+
imageResolution: "1280x720 (thumbnails)",
|
|
760
|
+
},
|
|
761
|
+
|
|
762
|
+
algorithm: {
|
|
763
|
+
topSignals: [
|
|
764
|
+
"Watch time / Average View Duration (AVD) — ~50% weight",
|
|
765
|
+
"Engagement rate (likes, comments, shares) — ~25%",
|
|
766
|
+
"Click-through rate (CTR from title+thumbnail) — ~25%",
|
|
767
|
+
"Satisfaction signals (surveys, repeat views, shares)",
|
|
768
|
+
"Session time (does your video lead to more watching?)",
|
|
769
|
+
"~70% of watch time comes from recommendations, not search",
|
|
770
|
+
],
|
|
771
|
+
penalizedActions: [
|
|
772
|
+
"Keyword stuffing in title/tags (NLP detects intent now)",
|
|
773
|
+
"Misleading thumbnails/titles (CTR without watch time hurts)",
|
|
774
|
+
"Irrelevant tags (verified by AI)",
|
|
775
|
+
"Clickbait without payoff",
|
|
776
|
+
"Long intros (viewers drop off before content starts)",
|
|
777
|
+
],
|
|
778
|
+
keyInsight: "Shorts and long-form are fully independent systems. Posting Shorts won't hurt/help long-form. Long-form (15+ min) is only 8% of uploads but accounts for 50% of total engagement.",
|
|
779
|
+
},
|
|
780
|
+
|
|
781
|
+
hashtags: {
|
|
782
|
+
optimal: "8-12 tags per video (only first 15 counted)",
|
|
783
|
+
placement: "Tags field + keywords naturally in description",
|
|
784
|
+
notes: "Tags carry less weight than title/description. YouTube AI verifies tag relevance — irrelevant tags are penalized.",
|
|
785
|
+
},
|
|
786
|
+
|
|
787
|
+
hooks: {
|
|
788
|
+
timeToHook: "Shorts: 3 seconds. Long-form: 30 seconds (70%+ retention target).",
|
|
789
|
+
bestFormulas: [
|
|
790
|
+
"Cliffhanger/question: 'I thought I understood YouTube... until this.'",
|
|
791
|
+
"Time-bound promise: 'In the next 60 seconds, I'll show you...'",
|
|
792
|
+
"Stakes-based: Make viewers feel the cost of NOT watching",
|
|
793
|
+
"Show a clip from later in the video (teaser)",
|
|
794
|
+
"Cut intros entirely or keep under 5 seconds",
|
|
795
|
+
],
|
|
796
|
+
avoid: [
|
|
797
|
+
"Logo intros",
|
|
798
|
+
"'Hey guys, welcome back' openings",
|
|
799
|
+
"Slow build-up without immediate value",
|
|
800
|
+
],
|
|
801
|
+
},
|
|
802
|
+
|
|
803
|
+
boostFeatures: [
|
|
804
|
+
"Thumbnail A/B testing (YouTube Studio Test & Compare, up to 3 variants)",
|
|
805
|
+
"Chapters/timestamps (appear in Google search as key moments)",
|
|
806
|
+
"End screens (extend session time — key satisfaction signal)",
|
|
807
|
+
"Community posts between uploads (2-4/week, polls perform best)",
|
|
808
|
+
"Hybrid strategy (Shorts + long-form = 3x faster sub growth)",
|
|
809
|
+
"'Hype' feature for channels 500-500K subs",
|
|
810
|
+
],
|
|
811
|
+
|
|
812
|
+
mistakes: [
|
|
813
|
+
"Not A/B testing thumbnails",
|
|
814
|
+
"Long intros or branding sequences",
|
|
815
|
+
"Ignoring Shorts (primary discovery engine)",
|
|
816
|
+
"Not saying keywords in spoken audio (auto-captions are indexed)",
|
|
817
|
+
"Posting less than 1/week (lose recommendation queue in 30-60 days)",
|
|
818
|
+
"Ignoring Community tab between uploads",
|
|
819
|
+
],
|
|
820
|
+
|
|
821
|
+
adSpecs: {
|
|
822
|
+
bestFormat: "Skippable in-stream or Shorts ads",
|
|
823
|
+
bestAspectRatio: "16:9 (in-stream), 9:16 (Shorts)",
|
|
824
|
+
bestResolution: "1920x1080 (in-stream), 1080x1920 (Shorts)",
|
|
825
|
+
headlineLength: "~30 characters",
|
|
826
|
+
bodyLength: "~90 characters",
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
|
|
830
|
+
// ─── Exports ─────────────────────────────────────────────────────────────────
|
|
831
|
+
|
|
832
|
+
export const ALL_PLATFORMS: PlatformSpec[] = [
|
|
833
|
+
FACEBOOK,
|
|
834
|
+
INSTAGRAM,
|
|
835
|
+
TIKTOK,
|
|
836
|
+
LINKEDIN,
|
|
837
|
+
X_TWITTER,
|
|
838
|
+
YOUTUBE,
|
|
839
|
+
];
|
|
840
|
+
|
|
841
|
+
export function getPlatform(key: string): PlatformSpec | undefined {
|
|
842
|
+
return ALL_PLATFORMS.find(p => p.key === key.toLowerCase());
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
export function getPlatformNames(): string[] {
|
|
846
|
+
return ALL_PLATFORMS.map(p => p.name);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// ─── Cross-Platform Summary (for system prompts) ────────────────────────────
|
|
850
|
+
|
|
851
|
+
export function buildPlatformSummary(platformKey: string): string {
|
|
852
|
+
const p = getPlatform(platformKey);
|
|
853
|
+
if (!p) return `Unknown platform: ${platformKey}`;
|
|
854
|
+
|
|
855
|
+
return `## ${p.name} Best Practices
|
|
856
|
+
- **Post when:** ${p.bestTimes.bestDays.join(", ")} at ${p.bestTimes.bestHours}
|
|
857
|
+
- **Frequency:** ${p.frequency.recommended}
|
|
858
|
+
- **Best format:** ${p.formats[0]?.name} (${p.formats[0]?.engagementRate} engagement)
|
|
859
|
+
- **Post length:** ${p.postSpecs.idealLength}
|
|
860
|
+
- **Hashtags:** ${p.hashtags.optimal}, ${p.hashtags.placement}
|
|
861
|
+
- **Video:** ${p.postSpecs.videoIdealLength} at ${p.postSpecs.videoAspectRatio}
|
|
862
|
+
- **Top algorithm signal:** ${p.algorithm.topSignals[0]}
|
|
863
|
+
- **Key insight:** ${p.algorithm.keyInsight}
|
|
864
|
+
- **Avoid:** ${p.mistakes.slice(0, 3).join("; ")}
|
|
865
|
+
- **Hook:** ${p.hooks.timeToHook}`;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
export function buildAllPlatformsSummary(): string {
|
|
869
|
+
return ALL_PLATFORMS.map(p => buildPlatformSummary(p.key)).join("\n\n");
|
|
870
|
+
}
|