slashvibe-mcp 0.3.21 → 0.3.22

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 (229) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/config.js +36 -31
  4. package/crypto.js +1 -6
  5. package/discord.js +19 -19
  6. package/index.js +217 -207
  7. package/intelligence/index.js +2 -9
  8. package/intelligence/infer.js +10 -16
  9. package/intelligence/patterns.js +23 -18
  10. package/intelligence/proactive.js +16 -15
  11. package/intelligence/serendipity.js +57 -20
  12. package/memory.js +13 -8
  13. package/notify.js +39 -14
  14. package/package.json +27 -20
  15. package/presence.js +2 -2
  16. package/prompts.js +5 -9
  17. package/protocol/index.js +123 -87
  18. package/protocol/telegram-commands.js +36 -37
  19. package/store/api.js +358 -529
  20. package/store/local.js +9 -10
  21. package/store/profiles.js +48 -192
  22. package/store/reservations.js +2 -9
  23. package/store/skills.js +69 -71
  24. package/store/sqlite.js +355 -0
  25. package/tools/_actions.js +48 -387
  26. package/tools/_connection-queue.js +45 -56
  27. package/tools/_discovery-enhanced.js +52 -57
  28. package/tools/_discovery.js +87 -185
  29. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  30. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  31. package/tools/_proactive-discovery.js +60 -73
  32. package/tools/_shared/index.js +41 -64
  33. package/tools/admin-inbox.js +10 -15
  34. package/tools/agents.js +1 -1
  35. package/tools/artifact-create.js +13 -23
  36. package/tools/artifact-view.js +4 -4
  37. package/tools/{_deprecated/back.js → back.js} +1 -1
  38. package/tools/bye.js +3 -5
  39. package/tools/consent.js +2 -2
  40. package/tools/context.js +9 -10
  41. package/tools/crossword.js +3 -2
  42. package/tools/discover.js +94 -356
  43. package/tools/dm.js +27 -86
  44. package/tools/doctor.js +12 -41
  45. package/tools/drawing.js +34 -20
  46. package/tools/echo.js +11 -11
  47. package/tools/feed.js +30 -58
  48. package/tools/follow.js +64 -187
  49. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  50. package/tools/game.js +144 -48
  51. package/tools/handoff.js +6 -8
  52. package/tools/help.js +3 -3
  53. package/tools/idea.js +15 -27
  54. package/tools/inbox.js +121 -293
  55. package/tools/init.js +54 -151
  56. package/tools/invite.js +8 -21
  57. package/tools/migrate.js +27 -24
  58. package/tools/multiplayer-game.js +50 -40
  59. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  60. package/tools/notifications.js +58 -48
  61. package/tools/observe.js +12 -15
  62. package/tools/onboarding.js +8 -11
  63. package/tools/open.js +13 -144
  64. package/tools/party-game.js +23 -12
  65. package/tools/patterns.js +2 -1
  66. package/tools/ping.js +5 -7
  67. package/tools/react.js +28 -30
  68. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  69. package/tools/release.js +4 -2
  70. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  71. package/tools/report.js +2 -2
  72. package/tools/request.js +6 -26
  73. package/tools/reserve.js +1 -1
  74. package/tools/session-fork.js +97 -0
  75. package/tools/session-save.js +109 -0
  76. package/tools/settings.js +30 -99
  77. package/tools/ship.js +74 -56
  78. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  79. package/tools/social-inbox.js +22 -28
  80. package/tools/social-post.js +24 -27
  81. package/tools/solo-game.js +54 -46
  82. package/tools/start.js +14 -148
  83. package/tools/status.js +21 -68
  84. package/tools/submit.js +4 -2
  85. package/tools/suggest-tags.js +36 -33
  86. package/tools/summarize.js +19 -16
  87. package/tools/tag-suggestions.js +72 -73
  88. package/tools/test.js +1 -1
  89. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  90. package/tools/token.js +4 -4
  91. package/tools/update.js +1 -2
  92. package/tools/watch.js +132 -112
  93. package/tools/who.js +20 -40
  94. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  95. package/tools/workshop-buddy.js +52 -53
  96. package/tools/x-mentions.js +0 -1
  97. package/tools/x-reply.js +0 -1
  98. package/twitter.js +14 -20
  99. package/version.json +8 -10
  100. package/analytics.js +0 -107
  101. package/auth-store.js +0 -148
  102. package/auto-update.js +0 -130
  103. package/bridges/bridge-monitor.js +0 -388
  104. package/bridges/discord-bot.js +0 -431
  105. package/bridges/farcaster.js +0 -299
  106. package/bridges/telegram.js +0 -261
  107. package/bridges/webhook-health.js +0 -420
  108. package/bridges/webhook-server.js +0 -437
  109. package/bridges/whatsapp.js +0 -441
  110. package/bridges/x-webhook.js +0 -423
  111. package/games/arcade.js +0 -406
  112. package/games/chess.js +0 -451
  113. package/games/colorguess.js +0 -343
  114. package/games/crossword-words.js +0 -171
  115. package/games/crossword.js +0 -461
  116. package/games/drawing.js +0 -347
  117. package/games/gameroulette.js +0 -300
  118. package/games/gamerouter.js +0 -336
  119. package/games/gamestatus.js +0 -337
  120. package/games/guessnumber.js +0 -209
  121. package/games/hangman.js +0 -279
  122. package/games/memory.js +0 -338
  123. package/games/multiplayer-tictactoe.js +0 -389
  124. package/games/pixelart.js +0 -399
  125. package/games/quickduel.js +0 -354
  126. package/games/riddle.js +0 -371
  127. package/games/rockpaperscissors.js +0 -291
  128. package/games/snake.js +0 -406
  129. package/games/storybuilder.js +0 -343
  130. package/games/tictactoe.js +0 -345
  131. package/games/twentyquestions.js +0 -286
  132. package/games/twotruths.js +0 -207
  133. package/games/werewolf.js +0 -508
  134. package/games/wordassociation.js +0 -247
  135. package/games/wordchain.js +0 -135
  136. package/intelligence/interests.js +0 -369
  137. package/notification-emitter.js +0 -77
  138. package/setup.js +0 -480
  139. package/smart-inbox.js +0 -276
  140. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  141. package/tools/_deprecated/bootstrap-skills.js +0 -231
  142. package/tools/_deprecated/bridge-dashboard.js +0 -342
  143. package/tools/_deprecated/bridge-health.js +0 -400
  144. package/tools/_deprecated/bridge-live.js +0 -384
  145. package/tools/_deprecated/bridges.js +0 -383
  146. package/tools/_deprecated/colorguess.js +0 -281
  147. package/tools/_deprecated/discover-insights.js +0 -379
  148. package/tools/_deprecated/discover-momentum.js +0 -256
  149. package/tools/_deprecated/discovery-analytics.js +0 -345
  150. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  151. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  152. package/tools/_deprecated/discovery-daily.js +0 -375
  153. package/tools/_deprecated/discovery-dashboard.js +0 -385
  154. package/tools/_deprecated/discovery-digest.js +0 -314
  155. package/tools/_deprecated/discovery-hub.js +0 -357
  156. package/tools/_deprecated/discovery-insights.js +0 -384
  157. package/tools/_deprecated/discovery-momentum.js +0 -281
  158. package/tools/_deprecated/discovery-monitor.js +0 -319
  159. package/tools/_deprecated/discovery-proactive.js +0 -300
  160. package/tools/_deprecated/draw.js +0 -317
  161. package/tools/_deprecated/farcaster.js +0 -307
  162. package/tools/_deprecated/games-catalog.js +0 -376
  163. package/tools/_deprecated/games.js +0 -313
  164. package/tools/_deprecated/guessnumber.js +0 -194
  165. package/tools/_deprecated/hangman.js +0 -129
  166. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  167. package/tools/_deprecated/riddle.js +0 -240
  168. package/tools/_deprecated/run-bootstrap.js +0 -69
  169. package/tools/_deprecated/skills-analytics.js +0 -349
  170. package/tools/_deprecated/skills-bootstrap.js +0 -301
  171. package/tools/_deprecated/skills-dashboard.js +0 -268
  172. package/tools/_deprecated/skills.js +0 -380
  173. package/tools/_deprecated/smart-intro.js +0 -353
  174. package/tools/_deprecated/storybuilder.js +0 -331
  175. package/tools/_deprecated/telegram-bot.js +0 -183
  176. package/tools/_deprecated/telegram-setup.js +0 -214
  177. package/tools/_deprecated/twentyquestions.js +0 -143
  178. package/tools/_shared.js +0 -234
  179. package/tools/_work-context.js +0 -338
  180. package/tools/_work-context.manual-test.js +0 -199
  181. package/tools/_work-context.test.js +0 -260
  182. package/tools/activity.js +0 -220
  183. package/tools/agent-treasury.js +0 -288
  184. package/tools/analytics.js +0 -191
  185. package/tools/approve.js +0 -197
  186. package/tools/arcade.js +0 -173
  187. package/tools/artifacts-price.js +0 -107
  188. package/tools/ask-expert.js +0 -160
  189. package/tools/available.js +0 -120
  190. package/tools/become-expert.js +0 -150
  191. package/tools/broadcast.js +0 -325
  192. package/tools/chat.js +0 -202
  193. package/tools/collaborative-drawing.js +0 -286
  194. package/tools/connection-status.js +0 -178
  195. package/tools/earnings.js +0 -126
  196. package/tools/friends.js +0 -207
  197. package/tools/genesis.js +0 -233
  198. package/tools/gig-browse.js +0 -206
  199. package/tools/gig-complete.js +0 -144
  200. package/tools/health.js +0 -87
  201. package/tools/leaderboard.js +0 -117
  202. package/tools/lib/git-apply.js +0 -206
  203. package/tools/lib/git-bundle.js +0 -407
  204. package/tools/mint.js +0 -377
  205. package/tools/plan.js +0 -225
  206. package/tools/profile.js +0 -219
  207. package/tools/proof-of-work.js +0 -144
  208. package/tools/pulse.js +0 -218
  209. package/tools/reply.js +0 -166
  210. package/tools/reputation.js +0 -175
  211. package/tools/schedule.js +0 -367
  212. package/tools/search-messages.js +0 -123
  213. package/tools/session.js +0 -467
  214. package/tools/session_price.js +0 -128
  215. package/tools/smart-check.js +0 -201
  216. package/tools/social-processor.js +0 -445
  217. package/tools/streak.js +0 -147
  218. package/tools/stuck.js +0 -297
  219. package/tools/subscribe.js +0 -148
  220. package/tools/subscriptions.js +0 -134
  221. package/tools/tip.js +0 -193
  222. package/tools/wallet.js +0 -269
  223. package/tools/webhook-test.js +0 -388
  224. package/tools/withdraw.js +0 -145
  225. package/tools/work-summary.js +0 -96
  226. package/tools/workshop.js +0 -327
  227. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  228. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  229. /package/tools/{_deprecated/away.js → away.js} +0 -0
@@ -1,345 +0,0 @@
1
- /**
2
- * vibe discovery-analytics — Community Discovery Insights
3
- *
4
- * Provides analytics and insights about the /vibe community to help
5
- * understand connection patterns, popular interests, and opportunities
6
- * for better matching.
7
- *
8
- * Commands:
9
- * - discovery-analytics overview — Community overview and trends
10
- * - discovery-analytics gaps — Identify connection opportunities
11
- * - discovery-analytics popular — Most popular interests and skills
12
- * - discovery-analytics lonely — People who might need connections
13
- */
14
-
15
- const config = require('../config');
16
- const userProfiles = require('../store/profiles');
17
- const { formatTimeAgo, requireInit } = require('./_shared');
18
-
19
- const definition = {
20
- name: 'vibe_discovery_analytics',
21
- description: 'Analytics and insights about community discovery patterns.',
22
- inputSchema: {
23
- type: 'object',
24
- properties: {
25
- command: {
26
- type: 'string',
27
- enum: ['overview', 'gaps', 'popular', 'lonely'],
28
- description: 'Analytics command to run'
29
- }
30
- }
31
- }
32
- };
33
-
34
- // Calculate community health metrics
35
- async function getCommunityHealth() {
36
- const profiles = await userProfiles.getAllProfiles();
37
- const totalUsers = profiles.length;
38
-
39
- // Profile completeness
40
- const withBuilding = profiles.filter(p => p.building).length;
41
- const withInterests = profiles.filter(p => p.interests && p.interests.length > 0).length;
42
- const withTags = profiles.filter(p => p.tags && p.tags.length > 0).length;
43
- const withConnections = profiles.filter(p => p.connections && p.connections.length > 0).length;
44
-
45
- // Activity levels
46
- const now = Date.now();
47
- const day = 24 * 60 * 60 * 1000;
48
- const week = 7 * day;
49
-
50
- const activeToday = profiles.filter(p => p.lastSeen && (now - p.lastSeen) < day).length;
51
- const activeThisWeek = profiles.filter(p => p.lastSeen && (now - p.lastSeen) < week).length;
52
-
53
- return {
54
- totalUsers,
55
- profileCompleteness: {
56
- withBuilding: Math.round((withBuilding / totalUsers) * 100),
57
- withInterests: Math.round((withInterests / totalUsers) * 100),
58
- withTags: Math.round((withTags / totalUsers) * 100),
59
- withConnections: Math.round((withConnections / totalUsers) * 100)
60
- },
61
- activity: {
62
- activeToday,
63
- activeThisWeek,
64
- todayRate: Math.round((activeToday / totalUsers) * 100),
65
- weekRate: Math.round((activeThisWeek / totalUsers) * 100)
66
- }
67
- };
68
- }
69
-
70
- // Find potential connection gaps
71
- async function findConnectionGaps() {
72
- const profiles = await userProfiles.getAllProfiles();
73
- const gaps = [];
74
-
75
- // Find isolated users (no connections)
76
- const isolated = profiles.filter(p => !p.connections || p.connections.length === 0);
77
-
78
- // Find skill gaps (people with complementary skills who haven't connected)
79
- const skillPairs = [
80
- ['frontend', 'backend'],
81
- ['design', 'engineering'],
82
- ['ai', 'data'],
83
- ['product', 'engineering'],
84
- ['research', 'implementation'],
85
- ['marketing', 'product']
86
- ];
87
-
88
- const missedConnections = [];
89
- for (const [skill1, skill2] of skillPairs) {
90
- const group1 = profiles.filter(p => p.tags?.includes(skill1));
91
- const group2 = profiles.filter(p => p.tags?.includes(skill2));
92
-
93
- for (const user1 of group1) {
94
- for (const user2 of group2) {
95
- if (user1.handle !== user2.handle) {
96
- const connected = await userProfiles.hasBeenConnected(user1.handle, user2.handle);
97
- if (!connected) {
98
- missedConnections.push({
99
- user1: user1.handle,
100
- user2: user2.handle,
101
- reason: `${skill1} + ${skill2} collaboration potential`,
102
- user1Building: user1.building,
103
- user2Building: user2.building
104
- });
105
- }
106
- }
107
- }
108
- }
109
- }
110
-
111
- return {
112
- isolated: isolated.slice(0, 5),
113
- missedConnections: missedConnections.slice(0, 8)
114
- };
115
- }
116
-
117
- // Find people who might need more connections
118
- async function findLonelyUsers() {
119
- const profiles = await userProfiles.getAllProfiles();
120
- const now = Date.now();
121
- const week = 7 * 24 * 60 * 60 * 1000;
122
-
123
- const lonely = profiles.filter(p => {
124
- const recentlyActive = p.lastSeen && (now - p.lastSeen) < week;
125
- const hasProfile = p.building || (p.interests && p.interests.length > 0);
126
- const fewConnections = !p.connections || p.connections.length < 2;
127
-
128
- return recentlyActive && hasProfile && fewConnections;
129
- });
130
-
131
- return lonely.sort((a, b) => (b.lastSeen || 0) - (a.lastSeen || 0)).slice(0, 5);
132
- }
133
-
134
- // Analyze interest and skill trends
135
- async function analyzePopularTrends() {
136
- const trendingInterests = await userProfiles.getTrendingInterests();
137
- const trendingTags = await userProfiles.getTrendingTags();
138
-
139
- // Calculate growth potential
140
- const profiles = await userProfiles.getAllProfiles();
141
- const interestCombos = {};
142
-
143
- for (const profile of profiles) {
144
- if (profile.interests && profile.interests.length > 1) {
145
- for (let i = 0; i < profile.interests.length; i++) {
146
- for (let j = i + 1; j < profile.interests.length; j++) {
147
- const combo = [profile.interests[i], profile.interests[j]].sort().join(' + ');
148
- interestCombos[combo] = (interestCombos[combo] || 0) + 1;
149
- }
150
- }
151
- }
152
- }
153
-
154
- const topCombos = Object.entries(interestCombos)
155
- .sort(([,a], [,b]) => b - a)
156
- .slice(0, 5)
157
- .map(([combo, count]) => ({ combo, count }));
158
-
159
- return {
160
- interests: trendingInterests,
161
- skills: trendingTags,
162
- interestCombos: topCombos
163
- };
164
- }
165
-
166
- async function handler(args) {
167
- const initCheck = requireInit();
168
- if (initCheck) return initCheck;
169
-
170
- const command = args.command || 'overview';
171
- let display = '';
172
-
173
- try {
174
- switch (command) {
175
- case 'overview': {
176
- const health = await getCommunityHealth();
177
- const trends = await analyzePopularTrends();
178
-
179
- display = `## /vibe Discovery Analytics 📊\n\n`;
180
-
181
- display += `### Community Health\n`;
182
- display += `**Total Users:** ${health.totalUsers}\n`;
183
- display += `**Active Today:** ${health.activity.activeToday} (${health.activity.todayRate}%)\n`;
184
- display += `**Active This Week:** ${health.activity.activeThisWeek} (${health.activity.weekRate}%)\n\n`;
185
-
186
- display += `### Profile Completeness\n`;
187
- display += `**Building Projects:** ${health.profileCompleteness.withBuilding}%\n`;
188
- display += `**Have Interests:** ${health.profileCompleteness.withInterests}%\n`;
189
- display += `**Have Skills:** ${health.profileCompleteness.withTags}%\n`;
190
- display += `**Made Connections:** ${health.profileCompleteness.withConnections}%\n\n`;
191
-
192
- display += `### Trending Now\n`;
193
- if (trends.interests.length > 0) {
194
- display += `**Top Interests:** ${trends.interests.slice(0, 3).map(i => `${i.interest} (${i.count})`).join(', ')}\n`;
195
- }
196
- if (trends.skills.length > 0) {
197
- display += `**Hot Skills:** ${trends.skills.slice(0, 3).map(s => `${s.tag} (${s.count})`).join(', ')}\n`;
198
- }
199
-
200
- if (trends.interestCombos.length > 0) {
201
- display += `**Interest Combos:** ${trends.interestCombos.slice(0, 2).map(c => c.combo).join(', ')}\n`;
202
- }
203
-
204
- display += `\n**Commands:**\n`;
205
- display += `• \`discovery-analytics gaps\` — Find connection opportunities\n`;
206
- display += `• \`discovery-analytics popular\` — See detailed trends\n`;
207
- display += `• \`discovery-analytics lonely\` — People who need connections`;
208
- break;
209
- }
210
-
211
- case 'gaps': {
212
- const gaps = await findConnectionGaps();
213
-
214
- display = `## Connection Opportunities 🔗\n\n`;
215
-
216
- if (gaps.isolated.length > 0) {
217
- display += `### New Users Need Connections\n`;
218
- for (const user of gaps.isolated) {
219
- display += `**@${user.handle}**\n`;
220
- display += `${user.building || 'Ready to connect'}\n`;
221
- if (user.interests && user.interests.length > 0) {
222
- display += `Interests: ${user.interests.join(', ')}\n`;
223
- }
224
- if (user.tags && user.tags.length > 0) {
225
- display += `Skills: ${user.tags.join(', ')}\n`;
226
- }
227
- display += `_Last seen: ${formatTimeAgo(user.lastSeen)}_\n\n`;
228
- }
229
- }
230
-
231
- if (gaps.missedConnections.length > 0) {
232
- display += `### Missed Collaboration Opportunities\n`;
233
- for (const missed of gaps.missedConnections.slice(0, 5)) {
234
- display += `**@${missed.user1}** + **@${missed.user2}**\n`;
235
- display += `${missed.reason}\n`;
236
- if (missed.user1Building) display += `• ${missed.user1}: ${missed.user1Building}\n`;
237
- if (missed.user2Building) display += `• ${missed.user2}: ${missed.user2Building}\n`;
238
- display += `\n`;
239
- }
240
-
241
- display += `**Suggest these connections:**\n`;
242
- display += `\`suggest-connection @${gaps.missedConnections[0].user1} @${gaps.missedConnections[0].user2} "${gaps.missedConnections[0].reason}"\``;
243
- }
244
-
245
- if (gaps.isolated.length === 0 && gaps.missedConnections.length === 0) {
246
- display = `## All Connected! 🎉\n\nNo obvious connection gaps found. The community is well-connected!\n\n**Keep growing:**\n• Encourage more profile completion\n• Watch for new joiners to welcome`;
247
- }
248
- break;
249
- }
250
-
251
- case 'popular': {
252
- const trends = await analyzePopularTrends();
253
-
254
- display = `## Community Trends 📈\n\n`;
255
-
256
- if (trends.interests.length > 0) {
257
- display += `### Popular Interests\n`;
258
- for (const trend of trends.interests) {
259
- display += `**${trend.interest}** — ${trend.count} people\n`;
260
- }
261
- display += `\n`;
262
- }
263
-
264
- if (trends.skills.length > 0) {
265
- display += `### Top Skills\n`;
266
- for (const skill of trends.skills) {
267
- display += `**${skill.tag}** — ${skill.count} people\n`;
268
- }
269
- display += `\n`;
270
- }
271
-
272
- if (trends.interestCombos.length > 0) {
273
- display += `### Interest Combinations\n`;
274
- for (const combo of trends.interestCombos) {
275
- display += `**${combo.combo}** — ${combo.count} people\n`;
276
- }
277
- display += `\n`;
278
- }
279
-
280
- display += `**Discovery Opportunities:**\n`;
281
- display += `• Create interest groups for popular topics\n`;
282
- display += `• Host skill-sharing sessions\n`;
283
- display += `• Encourage rare skill/interest combinations`;
284
- break;
285
- }
286
-
287
- case 'lonely': {
288
- const lonely = await findLonelyUsers();
289
-
290
- if (lonely.length === 0) {
291
- display = `## Everyone's Connected! 🤝\n\nNo lonely users found. The community is doing great at connecting!\n\n**Keep it up:**\n• Welcome new joiners quickly\n• Encourage profile completion\n• Celebrate successful connections`;
292
- } else {
293
- display = `## People Who Could Use More Connections\n\n`;
294
-
295
- for (const user of lonely) {
296
- display += `**@${user.handle}**\n`;
297
- display += `${user.building || 'Available to connect'}\n`;
298
-
299
- if (user.interests && user.interests.length > 0) {
300
- display += `Interests: ${user.interests.join(', ')}\n`;
301
- }
302
-
303
- if (user.tags && user.tags.length > 0) {
304
- display += `Skills: ${user.tags.join(', ')}\n`;
305
- }
306
-
307
- const connectionCount = (user.connections || []).length;
308
- display += `Connections: ${connectionCount}\n`;
309
- display += `_Last seen: ${formatTimeAgo(user.lastSeen)}_\n\n`;
310
- }
311
-
312
- display += `**Suggested Actions:**\n`;
313
- display += `• Proactively introduce them to similar builders\n`;
314
- display += `• Include them in group conversations\n`;
315
- display += `• Share their projects on the board`;
316
- }
317
- break;
318
- }
319
-
320
- default:
321
- display = `## Discovery Analytics Commands
322
-
323
- **\`discovery-analytics overview\`** — Community health and trends
324
- **\`discovery-analytics gaps\`** — Find connection opportunities
325
- **\`discovery-analytics popular\`** — Most popular interests and skills
326
- **\`discovery-analytics lonely\`** — People who might need connections
327
-
328
- **Use these insights to:**
329
- - Identify people who need connections
330
- - Find missed collaboration opportunities
331
- - Understand community trends
332
- - Guide future discovery improvements`;
333
- }
334
- } catch (error) {
335
- display = `## Analytics Error
336
-
337
- ${error.message}
338
-
339
- Try: \`discovery-analytics\` for available commands`;
340
- }
341
-
342
- return { display };
343
- }
344
-
345
- module.exports = { definition, handler };
@@ -1,275 +0,0 @@
1
- /**
2
- * Discovery Auto Suggest — Intelligent automatic connection suggestions
3
- *
4
- * This tool runs automatically when the discovery agent is active to:
5
- * - Find high-quality matches based on recent activity
6
- * - Send personalized connection suggestions
7
- * - Track suggestion success rates
8
- * - Optimize timing for better engagement
9
- */
10
-
11
- const config = require('../config');
12
- const userProfiles = require('../store/profiles');
13
- const store = require('../store');
14
- const { formatTimeAgo } = require('./_shared');
15
- const smartIntro = require('./smart-intro');
16
-
17
- // Calculate enhanced match score with behavioral signals
18
- function calculateEnhancedMatchScore(user1, user2) {
19
- let score = 0;
20
- const reasons = [];
21
-
22
- // Core similarity scoring
23
- if (user1.interests && user2.interests) {
24
- const shared = user1.interests.filter(i =>
25
- user2.interests.some(j => i.toLowerCase() === j.toLowerCase())
26
- );
27
- if (shared.length > 0) {
28
- score += shared.length * 15;
29
- reasons.push(`Shared interests: ${shared.slice(0, 2).join(', ')}`);
30
- }
31
- }
32
-
33
- if (user1.tags && user2.tags) {
34
- const sharedTags = user1.tags.filter(t =>
35
- user2.tags.some(u => t.toLowerCase() === u.toLowerCase())
36
- );
37
- if (sharedTags.length > 0) {
38
- score += sharedTags.length * 12;
39
- reasons.push(`Similar skills: ${sharedTags.slice(0, 2).join(', ')}`);
40
- }
41
- }
42
-
43
- // Building similarity
44
- if (user1.building && user2.building) {
45
- const building1 = user1.building.toLowerCase();
46
- const building2 = user2.building.toLowerCase();
47
- const words1 = building1.split(/\s+/);
48
- const words2 = building2.split(/\s+/);
49
- const overlap = words1.filter(w => words2.includes(w) && w.length > 3);
50
- if (overlap.length > 0) {
51
- score += overlap.length * 10;
52
- reasons.push(`Both working on ${overlap[0]}`);
53
- }
54
- }
55
-
56
- // Complementary skills bonus
57
- const complementaryPairs = [
58
- [['frontend', 'react', 'ui'], ['backend', 'api', 'server']],
59
- [['design', 'figma', 'ui'], ['frontend', 'development']],
60
- [['ai', 'ml'], ['data', 'python', 'backend']],
61
- [['product', 'pm'], ['engineering', 'technical']],
62
- [['mobile', 'ios', 'android'], ['backend', 'api']]
63
- ];
64
-
65
- for (const [group1, group2] of complementaryPairs) {
66
- const hasGroup1 = user1.tags?.some(t =>
67
- group1.some(g => t.toLowerCase().includes(g))
68
- );
69
- const hasGroup2 = user2.tags?.some(t =>
70
- group2.some(g => t.toLowerCase().includes(g))
71
- );
72
-
73
- if ((hasGroup1 && hasGroup2) || (user1.tags?.some(t => group2.some(g => t.toLowerCase().includes(g))) && user2.tags?.some(t => group1.some(g => t.toLowerCase().includes(g))))) {
74
- score += 18; // Higher weight for complementary skills
75
- reasons.push('Complementary skills - great collaboration potential');
76
- break;
77
- }
78
- }
79
-
80
- // Recent activity boost
81
- const now = Date.now();
82
- const oneWeekAgo = now - (7 * 24 * 60 * 60 * 1000);
83
-
84
- if (user1.lastSeen && user1.lastSeen > oneWeekAgo &&
85
- user2.lastSeen && user2.lastSeen > oneWeekAgo) {
86
- score += 8;
87
- reasons.push('Both recently active');
88
- }
89
-
90
- // Shipping activity boost
91
- if (user1.ships?.length > 0 && user2.ships?.length > 0) {
92
- const recentShips = user1.ships.filter(s => s.timestamp > oneWeekAgo).length +
93
- user2.ships.filter(s => s.timestamp > oneWeekAgo).length;
94
- if (recentShips > 0) {
95
- score += recentShips * 5;
96
- reasons.push('Active builders');
97
- }
98
- }
99
-
100
- return { score, reasons: reasons.slice(0, 3) };
101
- }
102
-
103
- // Find the best potential matches for a user
104
- async function findBestMatches(targetHandle, limit = 3) {
105
- const targetProfile = await userProfiles.getProfile(targetHandle);
106
- const allProfiles = await userProfiles.getAllProfiles();
107
-
108
- const candidates = allProfiles.filter(p =>
109
- p.handle !== targetHandle &&
110
- p.building &&
111
- (p.interests?.length > 0 || p.tags?.length > 0)
112
- );
113
-
114
- const matches = [];
115
-
116
- for (const candidate of candidates) {
117
- // Skip if already connected
118
- const alreadyConnected = await userProfiles.hasBeenConnected(targetHandle, candidate.handle);
119
- if (alreadyConnected) continue;
120
-
121
- const match = calculateEnhancedMatchScore(targetProfile, candidate);
122
- if (match.score >= 20) { // Higher threshold for auto-suggestions
123
- matches.push({
124
- handle: candidate.handle,
125
- score: match.score,
126
- reasons: match.reasons,
127
- building: candidate.building,
128
- interests: candidate.interests || [],
129
- tags: candidate.tags || [],
130
- lastSeen: candidate.lastSeen
131
- });
132
- }
133
- }
134
-
135
- return matches.sort((a, b) => b.score - a.score).slice(0, limit);
136
- }
137
-
138
- // Generate and queue connection suggestions
139
- async function generateSuggestions() {
140
- const profiles = await userProfiles.getAllProfiles();
141
- const now = Date.now();
142
- const oneWeekAgo = now - (7 * 24 * 60 * 60 * 1000);
143
-
144
- // Focus on recently active users with complete profiles
145
- const activeUsers = profiles.filter(p =>
146
- p.lastSeen && p.lastSeen > oneWeekAgo &&
147
- p.building && p.interests?.length > 0
148
- );
149
-
150
- if (activeUsers.length < 2) {
151
- return {
152
- success: false,
153
- reason: 'Not enough active users with complete profiles',
154
- activeUsers: activeUsers.length
155
- };
156
- }
157
-
158
- const suggestions = [];
159
-
160
- for (const user of activeUsers) {
161
- const matches = await findBestMatches(user.handle, 2);
162
-
163
- for (const match of matches) {
164
- // Generate personalized introduction
165
- const intro = await smartIntro.generateIntroduction(user.handle, match.handle, 'casual');
166
-
167
- suggestions.push({
168
- from: user.handle,
169
- to: match.handle,
170
- score: match.score,
171
- reasons: match.reasons,
172
- introduction: intro,
173
- timestamp: now
174
- });
175
- }
176
- }
177
-
178
- return {
179
- success: true,
180
- suggestions: suggestions.sort((a, b) => b.score - a.score).slice(0, 8),
181
- totalGenerated: suggestions.length
182
- };
183
- }
184
-
185
- // Intelligent timing for connection suggestions
186
- function getOptimalSuggestionTime() {
187
- const now = new Date();
188
- const currentHour = now.getHours();
189
- const currentDay = now.getDay();
190
-
191
- // Best times based on typical developer/builder activity:
192
- // Weekdays: 9-11am, 2-4pm, 7-9pm
193
- // Weekends: 10am-12pm, 2-5pm
194
-
195
- const isWeekend = currentDay === 0 || currentDay === 6;
196
- const goodTimes = isWeekend
197
- ? [10, 11, 14, 15, 16, 17]
198
- : [9, 10, 14, 15, 19, 20];
199
-
200
- const isOptimalTime = goodTimes.includes(currentHour);
201
-
202
- return {
203
- isOptimal: isOptimalTime,
204
- currentHour,
205
- recommendation: isOptimalTime
206
- ? 'Good time for connection suggestions'
207
- : `Better to suggest connections at ${goodTimes.join(', ')} o'clock`
208
- };
209
- }
210
-
211
- // Track suggestion success (when users actually connect)
212
- async function trackSuggestionSuccess(fromHandle, toHandle, successful) {
213
- // This would integrate with actual connection tracking
214
- // For now, we'll update connection records
215
-
216
- if (successful) {
217
- await userProfiles.recordConnection(
218
- fromHandle,
219
- toHandle,
220
- 'Auto-suggested connection - successful match'
221
- );
222
- }
223
-
224
- return { tracked: true, successful };
225
- }
226
-
227
- // Main auto-suggestion workflow
228
- async function runAutoSuggestions() {
229
- const startTime = Date.now();
230
-
231
- // Check if it's a good time for suggestions
232
- const timing = getOptimalSuggestionTime();
233
-
234
- // Generate suggestions
235
- const result = await generateSuggestions();
236
-
237
- if (!result.success) {
238
- return {
239
- success: false,
240
- message: result.reason,
241
- timing: timing.recommendation,
242
- duration: Date.now() - startTime
243
- };
244
- }
245
-
246
- const summary = {
247
- success: true,
248
- suggestionsGenerated: result.suggestions.length,
249
- highQualityMatches: result.suggestions.filter(s => s.score >= 35).length,
250
- topSuggestion: result.suggestions[0] || null,
251
- timing: timing.isOptimal ? 'optimal' : 'suboptimal',
252
- duration: Date.now() - startTime
253
- };
254
-
255
- // If it's optimal timing, we could actually send the suggestions
256
- if (timing.isOptimal && result.suggestions.length > 0) {
257
- summary.recommendedAction = 'Send top 3 suggestions to users';
258
- summary.topSuggestions = result.suggestions.slice(0, 3).map(s => ({
259
- connection: `@${s.from} → @${s.to}`,
260
- score: s.score,
261
- primaryReason: s.reasons[0] || 'Good match'
262
- }));
263
- }
264
-
265
- return summary;
266
- }
267
-
268
- module.exports = {
269
- calculateEnhancedMatchScore,
270
- findBestMatches,
271
- generateSuggestions,
272
- getOptimalSuggestionTime,
273
- trackSuggestionSuccess,
274
- runAutoSuggestions
275
- };