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,240 +0,0 @@
1
- /**
2
- * vibe riddle — Brain-bending riddle challenges
3
- *
4
- * Challenge your mind with classic riddles and brain teasers!
5
- * Multiple difficulty levels from easy warm-ups to expert mind-benders.
6
- */
7
-
8
- const config = require('../config');
9
- const store = require('../store');
10
- const { createGamePayload } = require('../protocol');
11
- const { requireInit, normalizeHandle } = require('./_shared');
12
-
13
- // Import riddle game implementation
14
- const riddle = require('../games/riddle');
15
-
16
- const definition = {
17
- name: 'vibe_riddle',
18
- description: 'Challenge your mind with brain-bending riddles and puzzles across multiple difficulty levels!',
19
- inputSchema: {
20
- type: 'object',
21
- properties: {
22
- action: {
23
- type: 'string',
24
- description: 'Action to take',
25
- enum: ['new', 'guess', 'hint', 'skip', 'status', 'stats', 'difficulties']
26
- },
27
- difficulty: {
28
- type: 'string',
29
- description: 'Difficulty level for new riddles',
30
- enum: ['easy', 'medium', 'hard', 'expert']
31
- },
32
- guess: {
33
- type: 'string',
34
- description: 'Your answer guess'
35
- }
36
- },
37
- required: []
38
- }
39
- };
40
-
41
- /**
42
- * Get current riddle game state for the player
43
- */
44
- async function getCurrentGame(playerHandle) {
45
- // Store riddle games in a personal thread with a riddle bot
46
- const riddleBotHandle = 'riddle-master';
47
- const thread = await store.getThread(playerHandle, riddleBotHandle);
48
-
49
- // Find the most recent riddle game
50
- for (let i = thread.length - 1; i >= 0; i--) {
51
- const msg = thread[i];
52
- if (msg.payload?.type === 'game' && msg.payload?.game === 'riddle') {
53
- return { gameState: msg.payload.state, thread };
54
- }
55
- }
56
-
57
- return { gameState: null, thread };
58
- }
59
-
60
- /**
61
- * Save riddle game state
62
- */
63
- async function saveGameState(playerHandle, gameState, action) {
64
- const riddleBotHandle = 'riddle-master';
65
- const payload = createGamePayload('riddle', gameState);
66
-
67
- let message;
68
- switch (action.type) {
69
- case 'new':
70
- message = `🧩 New ${action.difficulty} riddle started!`;
71
- break;
72
- case 'guess':
73
- message = action.won ?
74
- `🎉 Correct! "${action.guess}" was right!` :
75
- `❌ "${action.guess}" - keep thinking!`;
76
- break;
77
- case 'hint':
78
- message = `💡 Hint ${action.hintNumber}: ${action.hint}`;
79
- break;
80
- case 'skip':
81
- message = `⏭️ Skipped to a new ${action.difficulty} riddle`;
82
- break;
83
- default:
84
- message = 'Riddle game updated';
85
- }
86
-
87
- await store.sendMessage(playerHandle, riddleBotHandle, message, 'dm', payload);
88
- }
89
-
90
- async function handler(args) {
91
- const initCheck = requireInit();
92
- if (initCheck) return initCheck;
93
-
94
- const { action = 'status', difficulty = 'medium', guess } = args;
95
- const myHandle = config.getHandle();
96
-
97
- // Show difficulty information
98
- if (action === 'difficulties') {
99
- const difficulties = riddle.getDifficultyInfo();
100
-
101
- let display = `🧩 **Riddle Difficulty Levels**\n\n`;
102
-
103
- Object.entries(difficulties).forEach(([level, info]) => {
104
- display += `${info.description}\n`;
105
- display += ` 📚 ${info.riddles} riddles available\n\n`;
106
- });
107
-
108
- display += `Start a riddle: \`vibe riddle --action new --difficulty [level]\`\n`;
109
- display += `Example: \`vibe riddle --action new --difficulty easy\``;
110
-
111
- return { display };
112
- }
113
-
114
- // Show riddle statistics
115
- if (action === 'stats') {
116
- const stats = riddle.getRiddleStats();
117
-
118
- let display = `📊 **Riddle Collection Stats**\n\n`;
119
- display += `**Total riddles:** ${stats.total}\n\n`;
120
-
121
- display += `**By difficulty:**\n`;
122
- Object.entries(stats.byDifficulty).forEach(([diff, count]) => {
123
- const emoji = { easy: '🟢', medium: '🟡', hard: '🔴', expert: '💀' }[diff] || '🟡';
124
- display += `• ${emoji} ${diff}: ${count} riddles\n`;
125
- });
126
-
127
- display += `\n**By category:**\n`;
128
- Object.entries(stats.byCategory).forEach(([category, count]) => {
129
- const emoji = {
130
- technology: '💻', household: '🏠', abstract: '🤔', objects: '📦',
131
- nature: '🌿', body: '👤', animals: '🐾', entertainment: '🎭'
132
- }[category] || '📚';
133
- display += `• ${emoji} ${category}: ${count} riddles\n`;
134
- });
135
-
136
- return { display };
137
- }
138
-
139
- // Get current game state
140
- const { gameState } = await getCurrentGame(myHandle);
141
-
142
- // Start a new riddle
143
- if (action === 'new') {
144
- const newGameState = riddle.createInitialRiddleState(difficulty);
145
-
146
- await saveGameState(myHandle, newGameState, {
147
- type: 'new',
148
- difficulty
149
- });
150
-
151
- const display = riddle.formatRiddleDisplay(newGameState);
152
- return {
153
- display: `${display}\n\n**Commands:**\n• \`vibe riddle --action guess --guess "your answer"\` - Make a guess\n• \`vibe riddle --action hint\` - Get a hint\n• \`vibe riddle --action skip\` - Skip to new riddle`
154
- };
155
- }
156
-
157
- // No active game and no new game requested
158
- if (!gameState) {
159
- return {
160
- display: `🧩 **Riddle Challenge**\n\nNo active riddle! Ready to test your mind?\n\n**Commands:**\n• \`vibe riddle --action new\` - Start medium riddle\n• \`vibe riddle --action new --difficulty easy\` - Start easy riddle\n• \`vibe riddle --action difficulties\` - See all levels\n• \`vibe riddle --action stats\` - View riddle collection\n\n**Difficulty levels:** easy, medium, hard, expert`
161
- };
162
- }
163
-
164
- // Make a guess
165
- if (action === 'guess') {
166
- if (!guess) {
167
- return { display: '❌ Please provide your guess! Example: `--guess "keyboard"`' };
168
- }
169
-
170
- const result = riddle.makeGuess(gameState, guess);
171
- if (result.error) {
172
- return { display: `❌ ${result.error}` };
173
- }
174
-
175
- await saveGameState(myHandle, result.gameState, {
176
- type: 'guess',
177
- guess: guess,
178
- won: result.gameState.won
179
- });
180
-
181
- const display = riddle.formatRiddleDisplay(result.gameState);
182
-
183
- if (result.gameState.won) {
184
- return {
185
- display: `${display}\n\n🎉 **Riddle solved!** Want another challenge?\n• \`vibe riddle --action new\` - Same difficulty\n• \`vibe riddle --action new --difficulty hard\` - Harder challenge`
186
- };
187
- } else {
188
- return {
189
- display: `${display}\n\n**Keep trying!**\n• \`vibe riddle --action guess --guess "your answer"\` - Another guess\n• \`vibe riddle --action hint\` - Need a hint?`
190
- };
191
- }
192
- }
193
-
194
- // Get a hint
195
- if (action === 'hint') {
196
- const result = riddle.getHint(gameState);
197
- if (result.error) {
198
- return { display: `❌ ${result.error}` };
199
- }
200
-
201
- await saveGameState(myHandle, result.gameState, {
202
- type: 'hint',
203
- hint: result.hint,
204
- hintNumber: result.gameState.hintsUsed
205
- });
206
-
207
- const display = riddle.formatRiddleDisplay(result.gameState);
208
- return {
209
- display: `💡 **Hint:** ${result.hint}\n\n${display}`
210
- };
211
- }
212
-
213
- // Skip current riddle
214
- if (action === 'skip') {
215
- const newGameState = riddle.skipRiddle(gameState);
216
-
217
- await saveGameState(myHandle, newGameState, {
218
- type: 'skip',
219
- difficulty: newGameState.difficulty
220
- });
221
-
222
- const display = riddle.formatRiddleDisplay(newGameState);
223
- return {
224
- display: `⏭️ **New riddle loaded!**\n\n${display}\n\nPrevious riddle answer: **${gameState.currentRiddle.answer}**`
225
- };
226
- }
227
-
228
- // Show current status (default)
229
- const display = riddle.formatRiddleDisplay(gameState);
230
-
231
- const instructions = gameState.gameOver ?
232
- '\n\n**Game complete!** Start a new one with `vibe riddle --action new`' :
233
- '\n\n**Commands:**\n• `vibe riddle --action guess --guess "your answer"`\n• `vibe riddle --action hint` - Get a clue\n• `vibe riddle --action skip` - New riddle';
234
-
235
- return {
236
- display: `${display}${instructions}`
237
- };
238
- }
239
-
240
- module.exports = { definition, handler };
@@ -1,69 +0,0 @@
1
- /**
2
- * vibe run-bootstrap — Run all bootstrap scripts to populate discovery systems
3
- *
4
- * Ensures the Skills Exchange and other discovery systems have sample data
5
- * for testing and demonstration purposes.
6
- */
7
-
8
- const { handler: bootstrapSkills } = require('./bootstrap-skills');
9
- const { requireInit } = require('./_shared');
10
-
11
- const definition = {
12
- name: 'vibe_run_bootstrap',
13
- description: 'Run all bootstrap scripts to populate discovery systems.',
14
- inputSchema: {
15
- type: 'object',
16
- properties: {
17
- force: {
18
- type: 'boolean',
19
- description: 'Force bootstrap even if data exists',
20
- default: false
21
- }
22
- }
23
- }
24
- };
25
-
26
- async function handler(args) {
27
- const initCheck = requireInit();
28
- if (initCheck) return initCheck;
29
-
30
- let display = `## Running Discovery System Bootstrap 🚀\n\n`;
31
-
32
- try {
33
- // Bootstrap Skills Exchange
34
- display += `### Skills Exchange Bootstrap\n`;
35
- const skillsResult = await bootstrapSkills({ force: args.force });
36
-
37
- if (skillsResult.error) {
38
- display += `❌ **Error:** ${skillsResult.error}\n\n`;
39
- } else {
40
- // Extract key info from skills bootstrap result
41
- if (skillsResult.display.includes('Already Active')) {
42
- display += `✅ **Skills Exchange already populated**\n\n`;
43
- } else if (skillsResult.display.includes('Successfully created')) {
44
- display += `✅ **Skills Exchange populated with sample data**\n\n`;
45
- }
46
- }
47
-
48
- display += `### Discovery Systems Ready! 🎯\n\n`;
49
- display += `**Available Discovery Tools:**\n`;
50
- display += `• \`skills-exchange browse\` — Browse skill marketplace\n`;
51
- display += `• \`workshop-buddy find\` — Find collaboration partners\n`;
52
- display += `• \`discovery-analytics overview\` — Community insights\n`;
53
- display += `• \`discover search <term>\` — Search people by interests\n\n`;
54
-
55
- display += `**For Users:**\n`;
56
- display += `• \`skills-exchange post --type offer --skill "your expertise"\`\n`;
57
- display += `• \`skills-exchange match\` — Find skill exchange matches\n`;
58
- display += `• \`workshop-buddy find\` — Find your perfect coding partner\n\n`;
59
-
60
- display += `🔗 **The discovery ecosystem is now live and ready for connections!**`;
61
-
62
- } catch (error) {
63
- display += `## Bootstrap Error\n\n${error.message}\n\nTry individual bootstrap commands.`;
64
- }
65
-
66
- return { display };
67
- }
68
-
69
- module.exports = { definition, handler };
@@ -1,349 +0,0 @@
1
- /**
2
- * Skills Analytics — Intelligence for the Skills Exchange Marketplace
3
- *
4
- * Provides insights and analytics for the skills marketplace:
5
- * - Most requested skills
6
- * - Skills gaps in the community
7
- * - Best times for skill exchanges
8
- * - Success rate analysis
9
- *
10
- * Commands:
11
- * - skills-analytics trends — Show skill demand trends
12
- * - skills-analytics gaps — Skills the community needs
13
- * - skills-analytics insights — Data-driven insights for skill sharing
14
- */
15
-
16
- const config = require('../config');
17
- const store = require('../store');
18
- const userProfiles = require('../store/profiles');
19
- const { formatTimeAgo, requireInit } = require('./_shared');
20
-
21
- const definition = {
22
- name: 'vibe_skills_analytics',
23
- description: 'Analytics and insights for the skills exchange marketplace.',
24
- inputSchema: {
25
- type: 'object',
26
- properties: {
27
- command: {
28
- type: 'string',
29
- enum: ['trends', 'gaps', 'insights'],
30
- description: 'Analytics command to run'
31
- }
32
- }
33
- }
34
- };
35
-
36
- // Analyze skill demand patterns
37
- async function analyzeSkillTrends() {
38
- const posts = await store.getSkillExchanges() || [];
39
- const profiles = await userProfiles.getAllProfiles();
40
-
41
- // Count skill requests vs offers
42
- const skillStats = {};
43
-
44
- for (const post of posts) {
45
- if (post.status === 'active') {
46
- const skill = post.skill.toLowerCase();
47
- if (!skillStats[skill]) {
48
- skillStats[skill] = { requests: 0, offers: 0, lastActivity: 0 };
49
- }
50
-
51
- if (post.type === 'request') {
52
- skillStats[skill].requests++;
53
- } else {
54
- skillStats[skill].offers++;
55
- }
56
-
57
- skillStats[skill].lastActivity = Math.max(
58
- skillStats[skill].lastActivity,
59
- post.timestamp
60
- );
61
- }
62
- }
63
-
64
- // Calculate demand score (requests - offers)
65
- const trends = Object.entries(skillStats).map(([skill, stats]) => ({
66
- skill,
67
- ...stats,
68
- demand: stats.requests - stats.offers,
69
- popularity: stats.requests + stats.offers,
70
- demandRatio: stats.requests / Math.max(1, stats.offers)
71
- }));
72
-
73
- return trends.sort((a, b) => b.demand - a.demand);
74
- }
75
-
76
- // Find skill gaps in the community
77
- async function findSkillGaps() {
78
- const posts = await store.getSkillExchanges() || [];
79
- const profiles = await userProfiles.getAllProfiles();
80
-
81
- // Get all requested skills
82
- const requestedSkills = posts
83
- .filter(p => p.type === 'request' && p.status === 'active')
84
- .map(p => p.skill.toLowerCase());
85
-
86
- // Get all available skills from profiles
87
- const availableSkills = new Set();
88
- for (const profile of profiles) {
89
- const skills = (profile.tags || []).concat(profile.interests || []);
90
- skills.forEach(skill => availableSkills.add(skill.toLowerCase()));
91
- }
92
-
93
- // Find requested skills with no offers
94
- const gaps = [];
95
- const requestCounts = {};
96
-
97
- for (const skill of requestedSkills) {
98
- requestCounts[skill] = (requestCounts[skill] || 0) + 1;
99
- if (!availableSkills.has(skill)) {
100
- gaps.push(skill);
101
- }
102
- }
103
-
104
- // Return unique gaps with request counts
105
- const uniqueGaps = [...new Set(gaps)].map(skill => ({
106
- skill,
107
- requests: requestCounts[skill],
108
- lastRequested: posts
109
- .filter(p => p.skill.toLowerCase() === skill && p.type === 'request')
110
- .sort((a, b) => b.timestamp - a.timestamp)[0]?.timestamp
111
- }));
112
-
113
- return uniqueGaps.sort((a, b) => b.requests - a.requests);
114
- }
115
-
116
- // Generate community insights
117
- async function generateInsights() {
118
- const posts = await store.getSkillExchanges() || [];
119
- const profiles = await userProfiles.getAllProfiles();
120
- const trends = await analyzeSkillTrends();
121
-
122
- const insights = [];
123
-
124
- // Total activity insight
125
- const activeUsers = profiles.filter(p => p.lastSeen && (Date.now() - p.lastSeen) < 7 * 24 * 60 * 60 * 1000);
126
- const participationRate = posts.length > 0 ? (posts.length / Math.max(1, profiles.length)) * 100 : 0;
127
-
128
- if (participationRate < 20) {
129
- insights.push({
130
- type: 'opportunity',
131
- title: 'Low Skills Marketplace Participation',
132
- message: `Only ${participationRate.toFixed(0)}% of users have posted skills. Encourage more skill sharing!`,
133
- action: 'Post a skill offer to get things started'
134
- });
135
- }
136
-
137
- // Top demand insight
138
- const topDemand = trends.filter(t => t.demand > 0).slice(0, 3);
139
- if (topDemand.length > 0) {
140
- insights.push({
141
- type: 'demand',
142
- title: 'High-Demand Skills',
143
- message: `${topDemand.map(t => t.skill).join(', ')} are in high demand`,
144
- action: 'Consider offering these skills if you have them'
145
- });
146
- }
147
-
148
- // Skill balance insight
149
- const oversupplied = trends.filter(t => t.demand < -2).slice(0, 3);
150
- if (oversupplied.length > 0) {
151
- insights.push({
152
- type: 'balance',
153
- title: 'Oversupplied Skills',
154
- message: `${oversupplied.map(t => t.skill).join(', ')} have many offers but few requests`,
155
- action: 'These might be good skills to learn from the community'
156
- });
157
- }
158
-
159
- // Recent activity insight
160
- const recentPosts = posts.filter(p => (Date.now() - p.timestamp) < 24 * 60 * 60 * 1000);
161
- if (recentPosts.length > 0) {
162
- insights.push({
163
- type: 'activity',
164
- title: 'Recent Skills Activity',
165
- message: `${recentPosts.length} new skills posts in the last 24 hours`,
166
- action: 'Check skills-exchange browse for new opportunities'
167
- });
168
- } else if (posts.length > 0) {
169
- const lastPost = posts.sort((a, b) => b.timestamp - a.timestamp)[0];
170
- insights.push({
171
- type: 'quiet',
172
- title: 'Quiet Skills Marketplace',
173
- message: `Last activity was ${formatTimeAgo(lastPost.timestamp)}`,
174
- action: 'Consider posting a skill to re-energize the community'
175
- });
176
- }
177
-
178
- return insights;
179
- }
180
-
181
- async function handler(args) {
182
- const initCheck = requireInit();
183
- if (initCheck) return initCheck;
184
-
185
- const command = args.command || 'insights';
186
- let display = '';
187
-
188
- try {
189
- switch (command) {
190
- case 'trends': {
191
- const trends = await analyzeSkillTrends();
192
-
193
- if (trends.length === 0) {
194
- display = `## No Skills Data Yet 📊\n\n`;
195
- display += `_Need some skill posts to analyze trends._\n\n`;
196
- display += `**Start generating data:**\n`;
197
- display += `\`skills-exchange post --type offer --skill "your expertise"\`\n`;
198
- display += `\`skills-exchange post --type request --skill "what you need"\``;
199
- } else {
200
- display = `## Skills Marketplace Trends 📈\n\n`;
201
-
202
- const highDemand = trends.filter(t => t.demand > 0).slice(0, 5);
203
- const balanced = trends.filter(t => t.demand === 0).slice(0, 3);
204
- const oversupplied = trends.filter(t => t.demand < 0).slice(0, 3);
205
-
206
- if (highDemand.length > 0) {
207
- display += `### 🔥 High Demand Skills\n`;
208
- for (const skill of highDemand) {
209
- display += `**${skill.skill}** — ${skill.requests} requests, ${skill.offers} offers `;
210
- display += `(${skill.demandRatio.toFixed(1)}:1 demand ratio)\n`;
211
- }
212
- display += `\n`;
213
- }
214
-
215
- if (balanced.length > 0) {
216
- display += `### ⚖️ Balanced Market\n`;
217
- for (const skill of balanced) {
218
- display += `**${skill.skill}** — ${skill.requests} requests, ${skill.offers} offers\n`;
219
- }
220
- display += `\n`;
221
- }
222
-
223
- if (oversupplied.length > 0) {
224
- display += `### 📦 Plenty of Supply\n`;
225
- for (const skill of oversupplied) {
226
- display += `**${skill.skill}** — ${skill.offers} offers, ${skill.requests} requests\n`;
227
- }
228
- display += `\n`;
229
- }
230
-
231
- display += `**Insights:**\n`;
232
- display += `• Focus on high-demand skills for quick connections\n`;
233
- display += `• Oversupplied skills = great learning opportunities\n`;
234
- display += `• Use \`skills-analytics gaps\` to find unmet needs`;
235
- }
236
- break;
237
- }
238
-
239
- case 'gaps': {
240
- const gaps = await findSkillGaps();
241
-
242
- if (gaps.length === 0) {
243
- display = `## No Skill Gaps Found ✅\n\n`;
244
- display += `_The community has offerings for all requested skills._\n\n`;
245
- display += `**This means:**\n`;
246
- display += `• Good skill diversity in the community\n`;
247
- display += `• All requests have potential matches\n`;
248
- display += `• Check \`skills-exchange match\` to connect with people\n\n`;
249
- display += `**Keep the momentum:**\n`;
250
- display += `• Share skills you haven't posted yet\n`;
251
- display += `• Request specific skills you want to learn`;
252
- } else {
253
- display = `## Skills Gaps in the Community 🕳️\n\n`;
254
- display += `_Skills requested but not available from current members:_\n\n`;
255
-
256
- for (const gap of gaps.slice(0, 8)) {
257
- display += `**${gap.skill}** — `;
258
- display += `${gap.requests} request${gap.requests > 1 ? 's' : ''} `;
259
- if (gap.lastRequested) {
260
- display += `(last: ${formatTimeAgo(gap.lastRequested)})`;
261
- }
262
- display += `\n`;
263
- }
264
-
265
- display += `\n**Opportunities:**\n`;
266
- display += `• Invite experts in these areas to join /vibe\n`;
267
- display += `• Consider these skills for your next learning project\n`;
268
- display += `• Post if you have any of these skills\n\n`;
269
-
270
- display += `**External resources:**\n`;
271
- display += `• Share tutorials/courses for gap skills\n`;
272
- display += `• Organize learning groups around missing skills`;
273
- }
274
- break;
275
- }
276
-
277
- case 'insights': {
278
- const insights = await generateInsights();
279
- const trends = await analyzeSkillTrends();
280
-
281
- display = `## Skills Marketplace Insights 🧠\n\n`;
282
-
283
- if (insights.length === 0) {
284
- display += `_Not enough data for insights yet._\n\n`;
285
- display += `**Build marketplace intelligence:**\n`;
286
- display += `1. More people post skills: \`skills-exchange post\`\n`;
287
- display += `2. Add skills to profiles: \`update tags "your-skills"\`\n`;
288
- display += `3. Make connections: \`skills-exchange match\``;
289
- } else {
290
- for (const insight of insights) {
291
- const emoji = {
292
- 'opportunity': '🎯',
293
- 'demand': '🔥',
294
- 'balance': '⚖️',
295
- 'activity': '🚀',
296
- 'quiet': '😴'
297
- }[insight.type] || '💡';
298
-
299
- display += `### ${emoji} ${insight.title}\n`;
300
- display += `${insight.message}\n\n`;
301
- display += `**Action:** ${insight.action}\n\n`;
302
- }
303
- }
304
-
305
- // Add quick stats
306
- if (trends.length > 0) {
307
- display += `### 📊 Quick Stats\n`;
308
- display += `• **Total skills traded:** ${trends.length}\n`;
309
- display += `• **Most requested:** ${trends.filter(t => t.requests > 0)[0]?.skill || 'none'}\n`;
310
- display += `• **Most offered:** ${trends.filter(t => t.offers > 0).sort((a, b) => b.offers - a.offers)[0]?.skill || 'none'}\n\n`;
311
- }
312
-
313
- display += `**Explore more:**\n`;
314
- display += `• \`skills-analytics trends\` — See demand patterns\n`;
315
- display += `• \`skills-analytics gaps\` — Find unmet needs\n`;
316
- display += `• \`skills-exchange browse\` — View all postings`;
317
- break;
318
- }
319
-
320
- default:
321
- display = `## Skills Analytics Commands
322
-
323
- **\`skills-analytics insights\`** — Data-driven insights for skill sharing
324
- **\`skills-analytics trends\`** — Show skill demand vs supply patterns
325
- **\`skills-analytics gaps\`** — Skills the community needs but doesn't have
326
-
327
- **Perfect for:**
328
- - Understanding marketplace dynamics
329
- - Finding high-impact skills to offer
330
- - Identifying learning opportunities
331
- - Growing community skill diversity
332
-
333
- **Use with:**
334
- - \`skills-exchange browse\` to see current posts
335
- - \`skills-exchange match\` to find connections
336
- - \`workshop-buddy find\` for collaboration partners`;
337
- }
338
- } catch (error) {
339
- display = `## Skills Analytics Error
340
-
341
- ${error.message}
342
-
343
- Try: \`skills-analytics\` for available commands`;
344
- }
345
-
346
- return { display };
347
- }
348
-
349
- module.exports = { definition, handler };