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,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 };