slashvibe-mcp 0.3.21 → 0.3.23

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 (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/auto-update.js +10 -15
  4. package/config.js +36 -31
  5. package/crypto.js +1 -6
  6. package/debug.js +12 -0
  7. package/discord.js +19 -19
  8. package/eslint.config.js +54 -0
  9. package/index.js +217 -207
  10. package/intelligence/index.js +2 -9
  11. package/intelligence/infer.js +10 -16
  12. package/intelligence/patterns.js +23 -18
  13. package/intelligence/proactive.js +16 -15
  14. package/intelligence/serendipity.js +57 -20
  15. package/memory.js +13 -8
  16. package/migrate-v2.js +72 -0
  17. package/notification-emitter.js +2 -2
  18. package/notify.js +39 -14
  19. package/package.json +28 -29
  20. package/post-install.js +141 -0
  21. package/presence.js +2 -2
  22. package/prompts.js +5 -9
  23. package/protocol/index.js +123 -87
  24. package/protocol/telegram-commands.js +36 -37
  25. package/store/api.js +358 -529
  26. package/store/local.js +9 -10
  27. package/store/profiles.js +48 -192
  28. package/store/reservations.js +2 -9
  29. package/store/skills.js +69 -71
  30. package/store/sqlite.js +355 -0
  31. package/test-skills-bootstrap.js +20 -0
  32. package/test-v2-integration.js +385 -0
  33. package/tools/_actions.js +48 -387
  34. package/tools/_connection-queue.js +45 -56
  35. package/tools/_discovery-enhanced.js +52 -57
  36. package/tools/_discovery.js +87 -185
  37. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  38. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  39. package/tools/_proactive-discovery.js +60 -73
  40. package/tools/_shared/index.js +41 -64
  41. package/tools/admin-inbox.js +10 -15
  42. package/tools/agents.js +1 -1
  43. package/tools/artifact-create.js +13 -23
  44. package/tools/artifact-view.js +4 -4
  45. package/tools/{_deprecated/back.js → back.js} +1 -1
  46. package/tools/bye.js +3 -5
  47. package/tools/consent.js +2 -2
  48. package/tools/context.js +9 -10
  49. package/tools/crossword.js +3 -2
  50. package/tools/discover.js +94 -356
  51. package/tools/dm.js +27 -86
  52. package/tools/doctor.js +12 -41
  53. package/tools/drawing.js +34 -20
  54. package/tools/echo.js +11 -11
  55. package/tools/feed.js +30 -58
  56. package/tools/follow.js +64 -187
  57. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  58. package/tools/game.js +144 -48
  59. package/tools/handoff.js +6 -8
  60. package/tools/help.js +3 -3
  61. package/tools/idea.js +15 -27
  62. package/tools/inbox.js +121 -293
  63. package/tools/init.js +54 -151
  64. package/tools/invite.js +8 -21
  65. package/tools/migrate.js +27 -24
  66. package/tools/multiplayer-game.js +50 -40
  67. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  68. package/tools/notifications.js +58 -48
  69. package/tools/observe.js +12 -15
  70. package/tools/onboarding.js +8 -11
  71. package/tools/open.js +13 -144
  72. package/tools/party-game.js +23 -12
  73. package/tools/patterns.js +2 -1
  74. package/tools/ping.js +5 -7
  75. package/tools/react.js +28 -30
  76. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  77. package/tools/release.js +4 -2
  78. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  79. package/tools/report.js +2 -2
  80. package/tools/request.js +6 -26
  81. package/tools/reserve.js +1 -1
  82. package/tools/session-fork.js +97 -0
  83. package/tools/session-save.js +109 -0
  84. package/tools/settings.js +30 -99
  85. package/tools/ship.js +74 -56
  86. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  87. package/tools/social-inbox.js +22 -28
  88. package/tools/social-post.js +24 -27
  89. package/tools/solo-game.js +54 -46
  90. package/tools/start.js +14 -148
  91. package/tools/status.js +21 -68
  92. package/tools/submit.js +4 -2
  93. package/tools/suggest-tags.js +36 -33
  94. package/tools/summarize.js +19 -16
  95. package/tools/tag-suggestions.js +72 -73
  96. package/tools/test.js +1 -1
  97. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  98. package/tools/token.js +4 -4
  99. package/tools/update.js +1 -2
  100. package/tools/watch.js +132 -112
  101. package/tools/who.js +20 -40
  102. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  103. package/tools/workshop-buddy.js +52 -53
  104. package/tools/x-mentions.js +0 -1
  105. package/tools/x-reply.js +0 -1
  106. package/twitter.js +14 -20
  107. package/version.json +8 -10
  108. package/webhook-runner.js +132 -0
  109. package/auth-store.js +0 -148
  110. package/bridges/bridge-monitor.js +0 -388
  111. package/bridges/discord-bot.js +0 -431
  112. package/bridges/farcaster.js +0 -299
  113. package/bridges/telegram.js +0 -261
  114. package/bridges/webhook-health.js +0 -420
  115. package/bridges/webhook-server.js +0 -437
  116. package/bridges/whatsapp.js +0 -441
  117. package/bridges/x-webhook.js +0 -423
  118. package/games/arcade.js +0 -406
  119. package/games/chess.js +0 -451
  120. package/games/colorguess.js +0 -343
  121. package/games/crossword-words.js +0 -171
  122. package/games/crossword.js +0 -461
  123. package/games/drawing.js +0 -347
  124. package/games/gameroulette.js +0 -300
  125. package/games/gamerouter.js +0 -336
  126. package/games/gamestatus.js +0 -337
  127. package/games/guessnumber.js +0 -209
  128. package/games/hangman.js +0 -279
  129. package/games/memory.js +0 -338
  130. package/games/multiplayer-tictactoe.js +0 -389
  131. package/games/pixelart.js +0 -399
  132. package/games/quickduel.js +0 -354
  133. package/games/riddle.js +0 -371
  134. package/games/rockpaperscissors.js +0 -291
  135. package/games/snake.js +0 -406
  136. package/games/storybuilder.js +0 -343
  137. package/games/tictactoe.js +0 -345
  138. package/games/twentyquestions.js +0 -286
  139. package/games/twotruths.js +0 -207
  140. package/games/werewolf.js +0 -508
  141. package/games/wordassociation.js +0 -247
  142. package/games/wordchain.js +0 -135
  143. package/intelligence/interests.js +0 -369
  144. package/setup.js +0 -480
  145. package/smart-inbox.js +0 -276
  146. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  147. package/tools/_deprecated/bootstrap-skills.js +0 -231
  148. package/tools/_deprecated/bridge-dashboard.js +0 -342
  149. package/tools/_deprecated/bridge-health.js +0 -400
  150. package/tools/_deprecated/bridge-live.js +0 -384
  151. package/tools/_deprecated/bridges.js +0 -383
  152. package/tools/_deprecated/colorguess.js +0 -281
  153. package/tools/_deprecated/discover-insights.js +0 -379
  154. package/tools/_deprecated/discover-momentum.js +0 -256
  155. package/tools/_deprecated/discovery-analytics.js +0 -345
  156. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  157. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  158. package/tools/_deprecated/discovery-daily.js +0 -375
  159. package/tools/_deprecated/discovery-dashboard.js +0 -385
  160. package/tools/_deprecated/discovery-digest.js +0 -314
  161. package/tools/_deprecated/discovery-hub.js +0 -357
  162. package/tools/_deprecated/discovery-insights.js +0 -384
  163. package/tools/_deprecated/discovery-momentum.js +0 -281
  164. package/tools/_deprecated/discovery-monitor.js +0 -319
  165. package/tools/_deprecated/discovery-proactive.js +0 -300
  166. package/tools/_deprecated/draw.js +0 -317
  167. package/tools/_deprecated/farcaster.js +0 -307
  168. package/tools/_deprecated/games-catalog.js +0 -376
  169. package/tools/_deprecated/games.js +0 -313
  170. package/tools/_deprecated/guessnumber.js +0 -194
  171. package/tools/_deprecated/hangman.js +0 -129
  172. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  173. package/tools/_deprecated/riddle.js +0 -240
  174. package/tools/_deprecated/run-bootstrap.js +0 -69
  175. package/tools/_deprecated/skills-analytics.js +0 -349
  176. package/tools/_deprecated/skills-bootstrap.js +0 -301
  177. package/tools/_deprecated/skills-dashboard.js +0 -268
  178. package/tools/_deprecated/skills.js +0 -380
  179. package/tools/_deprecated/smart-intro.js +0 -353
  180. package/tools/_deprecated/storybuilder.js +0 -331
  181. package/tools/_deprecated/telegram-bot.js +0 -183
  182. package/tools/_deprecated/telegram-setup.js +0 -214
  183. package/tools/_deprecated/twentyquestions.js +0 -143
  184. package/tools/_shared.js +0 -234
  185. package/tools/_work-context.js +0 -338
  186. package/tools/_work-context.manual-test.js +0 -199
  187. package/tools/_work-context.test.js +0 -260
  188. package/tools/activity.js +0 -220
  189. package/tools/agent-treasury.js +0 -288
  190. package/tools/analytics.js +0 -191
  191. package/tools/approve.js +0 -197
  192. package/tools/arcade.js +0 -173
  193. package/tools/artifacts-price.js +0 -107
  194. package/tools/ask-expert.js +0 -160
  195. package/tools/available.js +0 -120
  196. package/tools/become-expert.js +0 -150
  197. package/tools/broadcast.js +0 -325
  198. package/tools/chat.js +0 -202
  199. package/tools/collaborative-drawing.js +0 -286
  200. package/tools/connection-status.js +0 -178
  201. package/tools/earnings.js +0 -126
  202. package/tools/friends.js +0 -207
  203. package/tools/genesis.js +0 -233
  204. package/tools/gig-browse.js +0 -206
  205. package/tools/gig-complete.js +0 -144
  206. package/tools/health.js +0 -87
  207. package/tools/leaderboard.js +0 -117
  208. package/tools/lib/git-apply.js +0 -206
  209. package/tools/lib/git-bundle.js +0 -407
  210. package/tools/mint.js +0 -377
  211. package/tools/plan.js +0 -225
  212. package/tools/profile.js +0 -219
  213. package/tools/proof-of-work.js +0 -144
  214. package/tools/pulse.js +0 -218
  215. package/tools/reply.js +0 -166
  216. package/tools/reputation.js +0 -175
  217. package/tools/schedule.js +0 -367
  218. package/tools/search-messages.js +0 -123
  219. package/tools/session.js +0 -467
  220. package/tools/session_price.js +0 -128
  221. package/tools/smart-check.js +0 -201
  222. package/tools/social-processor.js +0 -445
  223. package/tools/streak.js +0 -147
  224. package/tools/stuck.js +0 -297
  225. package/tools/subscribe.js +0 -148
  226. package/tools/subscriptions.js +0 -134
  227. package/tools/tip.js +0 -193
  228. package/tools/wallet.js +0 -269
  229. package/tools/webhook-test.js +0 -388
  230. package/tools/withdraw.js +0 -145
  231. package/tools/work-summary.js +0 -96
  232. package/tools/workshop.js +0 -327
  233. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  234. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  235. /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
- };