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,343 +0,0 @@
1
- /**
2
- * Story Builder game implementation for /vibe
3
- * A collaborative storytelling game where players take turns adding one sentence
4
- * Build creative stories together with friends!
5
- */
6
-
7
- // Story prompts to help start interesting stories
8
- const STORY_PROMPTS = [
9
- "It was a dark and stormy night when the mysterious package arrived...",
10
- "The old lighthouse keeper discovered something impossible in the basement...",
11
- "Sarah woke up to find that everyone in town had vanished except her...",
12
- "The time machine worked perfectly, except for one small problem...",
13
- "The AI assistant started behaving strangely after the update...",
14
- "In the year 2087, the last library on Earth received an unexpected visitor...",
15
- "The cat came home wearing a tiny suit and carrying a briefcase...",
16
- "When the elevator doors opened on floor 13, it wasn't floor 13 at all...",
17
- "The dragon apologized profusely for burning down the village...",
18
- "Every mirror in the house started showing a different reflection...",
19
- "The food truck only appeared at midnight, and only sold memories...",
20
- "After 20 years in space, the astronaut returned to find Earth very different...",
21
- "The magic spell worked, but not quite as intended...",
22
- "The robot barista made the perfect coffee, but refused to serve humans...",
23
- "Deep in the Amazon, the explorer found a city that didn't exist on any map..."
24
- ];
25
-
26
- // Story genres and their characteristics
27
- const GENRES = {
28
- mystery: {
29
- name: "Mystery",
30
- prompts: [
31
- "The detective found a clue that changed everything...",
32
- "The locked room had no windows, no secret passages, yet someone had escaped...",
33
- "The witness's story had one fatal flaw..."
34
- ],
35
- endings: ["Who was the real culprit?", "What was the missing piece?", "How did they solve the case?"]
36
- },
37
- scifi: {
38
- name: "Science Fiction",
39
- prompts: [
40
- "The spacecraft's AI began questioning its own programming...",
41
- "The new planet looked perfect for colonization, until they discovered...",
42
- "Time travel was possible, but came with unexpected consequences..."
43
- ],
44
- endings: ["What did technology make possible?", "How did they adapt to the future?", "What was discovered in space?"]
45
- },
46
- fantasy: {
47
- name: "Fantasy",
48
- prompts: [
49
- "The ancient prophecy was coming true, but backwards...",
50
- "The wizard's apprentice accidentally unleashed something from the forbidden grimoire...",
51
- "The enchanted forest began whispering warnings to travelers..."
52
- ],
53
- endings: ["What magical power was revealed?", "How was the kingdom saved?", "What did the prophecy really mean?"]
54
- },
55
- comedy: {
56
- name: "Comedy",
57
- prompts: [
58
- "The superhero's greatest weakness turned out to be absolutely ridiculous...",
59
- "The cooking show went horribly wrong when the mystery ingredient was revealed...",
60
- "The world's worst spy somehow kept succeeding by accident..."
61
- ],
62
- endings: ["What was the hilarious misunderstanding?", "How did chaos lead to success?", "What was the ridiculous twist?"]
63
- }
64
- };
65
-
66
- // Create initial story builder state
67
- function createInitialStoryBuilderState(genre = null, customPrompt = null) {
68
- let prompt;
69
- let selectedGenre = 'general';
70
-
71
- if (customPrompt) {
72
- prompt = customPrompt;
73
- selectedGenre = 'custom';
74
- } else if (genre && GENRES[genre]) {
75
- selectedGenre = genre;
76
- const genrePrompts = GENRES[genre].prompts;
77
- prompt = genrePrompts[Math.floor(Math.random() * genrePrompts.length)];
78
- } else {
79
- prompt = STORY_PROMPTS[Math.floor(Math.random() * STORY_PROMPTS.length)];
80
- }
81
-
82
- return {
83
- sentences: [prompt],
84
- players: [],
85
- currentPlayer: null,
86
- moves: 1, // Prompt counts as move 1
87
- gameOver: false,
88
- maxSentences: 20, // End after 20 sentences for now
89
- genre: selectedGenre,
90
- playerTurnIndex: 0,
91
- prompt: prompt,
92
- createdAt: new Date().toISOString(),
93
- lastActivity: new Date().toISOString(),
94
- wordCount: prompt.split(' ').length
95
- };
96
- }
97
-
98
- // Add player to story
99
- function addPlayer(gameState, playerHandle) {
100
- if (gameState.players.includes(playerHandle)) {
101
- return { error: 'Player already in the story!' };
102
- }
103
-
104
- if (gameState.players.length >= 8) {
105
- return { error: 'Story is full (max 8 storytellers)' };
106
- }
107
-
108
- const newPlayers = [...gameState.players, playerHandle];
109
-
110
- return {
111
- success: true,
112
- gameState: {
113
- ...gameState,
114
- players: newPlayers,
115
- currentPlayer: gameState.currentPlayer || playerHandle // First player starts
116
- }
117
- };
118
- }
119
-
120
- // Add a sentence to the story
121
- function addSentence(gameState, sentence, playerHandle) {
122
- const { sentences, players, currentPlayer, moves, maxSentences } = gameState;
123
-
124
- // Clean and validate sentence
125
- const cleanSentence = sentence.trim();
126
-
127
- if (!cleanSentence) {
128
- return { error: 'Sentence cannot be empty!' };
129
- }
130
-
131
- if (cleanSentence.length > 500) {
132
- return { error: 'Sentence too long! Keep it under 500 characters.' };
133
- }
134
-
135
- // Check if it's the right player's turn (if multiplayer)
136
- if (players.length > 1 && currentPlayer !== playerHandle) {
137
- return { error: `Not your turn! Waiting for @${currentPlayer}` };
138
- }
139
-
140
- // Add proper punctuation if missing
141
- let finalSentence = cleanSentence;
142
- if (!/[.!?]$/.test(finalSentence)) {
143
- finalSentence += '.';
144
- }
145
-
146
- // Update story
147
- const newSentences = [...sentences, finalSentence];
148
- const newMoves = moves + 1;
149
- const newWordCount = gameState.wordCount + finalSentence.split(' ').length;
150
-
151
- // Determine next player
152
- let nextPlayerIndex = gameState.playerTurnIndex;
153
- let nextPlayer = currentPlayer;
154
-
155
- if (players.length > 1) {
156
- nextPlayerIndex = (gameState.playerTurnIndex + 1) % players.length;
157
- nextPlayer = players[nextPlayerIndex];
158
- }
159
-
160
- // Check if story should end
161
- const shouldEnd = newMoves >= maxSentences;
162
-
163
- const newGameState = {
164
- ...gameState,
165
- sentences: newSentences,
166
- currentPlayer: shouldEnd ? null : nextPlayer,
167
- playerTurnIndex: nextPlayerIndex,
168
- moves: newMoves,
169
- gameOver: shouldEnd,
170
- lastActivity: new Date().toISOString(),
171
- wordCount: newWordCount,
172
- lastContributor: playerHandle
173
- };
174
-
175
- return { success: true, gameState: newGameState };
176
- }
177
-
178
- // Format story builder display
179
- function formatStoryBuilderDisplay(gameState) {
180
- const { sentences, players, currentPlayer, moves, gameOver, maxSentences, genre, wordCount, prompt } = gameState;
181
-
182
- let display = `📖 **Collaborative Story** (${moves}/${maxSentences} sentences)\n\n`;
183
-
184
- if (genre !== 'general' && genre !== 'custom') {
185
- display += `**Genre:** ${GENRES[genre]?.name || genre}\n\n`;
186
- }
187
-
188
- if (gameOver) {
189
- display += '🎉 **Story Complete!** What an adventure!\n\n';
190
- }
191
-
192
- // Show the story
193
- display += '**The Story So Far:**\n';
194
- display += '```\n';
195
-
196
- // Show all sentences with paragraph breaks every 3-4 sentences
197
- let storyText = '';
198
- for (let i = 0; i < sentences.length; i++) {
199
- storyText += sentences[i];
200
-
201
- // Add paragraph break every 3-4 sentences for readability
202
- if ((i + 1) % 3 === 0 && i < sentences.length - 1) {
203
- storyText += '\n\n';
204
- } else if (i < sentences.length - 1) {
205
- storyText += ' ';
206
- }
207
- }
208
-
209
- display += storyText + '\n```\n\n';
210
-
211
- // Show stats
212
- display += `**Stats:** ${sentences.length} sentences, ${wordCount} words\n\n`;
213
-
214
- // Show players if multiplayer
215
- if (players.length > 1) {
216
- display += `**Storytellers (${players.length}):** ${players.map(p => `@${p}`).join(', ')}\n\n`;
217
- }
218
-
219
- if (!gameOver) {
220
- if (players.length > 1) {
221
- display += `**@${currentPlayer}, continue the story!**\n`;
222
- } else if (players.length === 1) {
223
- display += '**Your turn! Add the next sentence.**\n';
224
- } else {
225
- display += '**Join the story and add the next sentence!**\n';
226
- }
227
-
228
- // Show helpful prompts based on story length
229
- if (sentences.length < 5) {
230
- display += 'Building the scene...';
231
- } else if (sentences.length < 10) {
232
- display += 'Developing the plot...';
233
- } else if (sentences.length < 15) {
234
- display += 'Heading toward the climax...';
235
- } else {
236
- display += 'Time to wrap up the story!';
237
- }
238
- } else {
239
- // Show completion message with genre-specific ending questions
240
- if (genre && GENRES[genre] && GENRES[genre].endings) {
241
- const endings = GENRES[genre].endings;
242
- const randomEnding = endings[Math.floor(Math.random() * endings.length)];
243
- display += `**Reflection:** ${randomEnding}`;
244
- } else {
245
- display += '**What a creative journey! Thanks to all the storytellers!**';
246
- }
247
- }
248
-
249
- return display;
250
- }
251
-
252
- // Get story statistics
253
- function getStoryStats(gameState) {
254
- const { sentences, players } = gameState;
255
-
256
- if (sentences.length <= 1) return null; // Just the prompt
257
-
258
- // Calculate statistics
259
- const totalWords = gameState.wordCount;
260
- const avgWordsPerSentence = Math.round(totalWords / sentences.length);
261
-
262
- // Find longest and shortest sentences
263
- let longest = '';
264
- let shortest = sentences[0];
265
-
266
- for (const sentence of sentences) {
267
- if (sentence.length > longest.length) {
268
- longest = sentence;
269
- }
270
- if (sentence.length < shortest.length && sentence !== gameState.prompt) {
271
- shortest = sentence;
272
- }
273
- }
274
-
275
- return {
276
- totalSentences: sentences.length,
277
- totalWords: totalWords,
278
- avgWordsPerSentence,
279
- longestSentence: longest.substring(0, 50) + (longest.length > 50 ? '...' : ''),
280
- shortestSentence: shortest.substring(0, 50) + (shortest.length > 50 ? '...' : ''),
281
- contributors: players.length
282
- };
283
- }
284
-
285
- // Suggest story themes based on content
286
- function analyzeStoryThemes(gameState) {
287
- const { sentences } = gameState;
288
- const storyText = sentences.join(' ').toLowerCase();
289
-
290
- const themes = [];
291
-
292
- // Look for common themes
293
- if (/(magic|wizard|spell|dragon|knight|castle|potion)/.test(storyText)) {
294
- themes.push('🏰 Fantasy Adventure');
295
- }
296
-
297
- if (/(space|robot|future|technology|alien|planet|laser)/.test(storyText)) {
298
- themes.push('🚀 Science Fiction');
299
- }
300
-
301
- if (/(detective|mystery|clue|suspect|murder|crime)/.test(storyText)) {
302
- themes.push('🔍 Mystery & Crime');
303
- }
304
-
305
- if (/(funny|hilarious|ridiculous|silly|comedy|laugh)/.test(storyText)) {
306
- themes.push('😂 Comedy');
307
- }
308
-
309
- if (/(love|romance|heart|kiss|wedding|date)/.test(storyText)) {
310
- themes.push('💕 Romance');
311
- }
312
-
313
- if (/(ghost|spirit|haunted|scary|supernatural|witch)/.test(storyText)) {
314
- themes.push('👻 Supernatural');
315
- }
316
-
317
- if (/(adventure|journey|travel|explore|quest|treasure)/.test(storyText)) {
318
- themes.push('🗺️ Adventure');
319
- }
320
-
321
- return themes;
322
- }
323
-
324
- // Get random prompt by genre
325
- function getRandomPrompt(genre = null) {
326
- if (genre && GENRES[genre]) {
327
- const prompts = GENRES[genre].prompts;
328
- return prompts[Math.floor(Math.random() * prompts.length)];
329
- }
330
- return STORY_PROMPTS[Math.floor(Math.random() * STORY_PROMPTS.length)];
331
- }
332
-
333
- module.exports = {
334
- createInitialStoryBuilderState,
335
- addPlayer,
336
- addSentence,
337
- formatStoryBuilderDisplay,
338
- getStoryStats,
339
- analyzeStoryThemes,
340
- getRandomPrompt,
341
- STORY_PROMPTS,
342
- GENRES
343
- };
@@ -1,345 +0,0 @@
1
- /**
2
- * Tic-Tac-Toe game implementation for /vibe
3
- * Classic 3x3 grid game with AI opponent support
4
- * Includes multiple difficulty levels: easy, medium, hard
5
- */
6
-
7
- // Create initial tic-tac-toe state
8
- function createInitialTicTacToeState(difficulty = 'medium') {
9
- return {
10
- board: Array(9).fill(''), // 3x3 grid as flat array
11
- turn: 'X', // X always goes first
12
- moves: 0,
13
- winner: null,
14
- gameOver: false,
15
- isDraw: false,
16
- playerSymbol: 'X', // Player is X, AI is O
17
- aiSymbol: 'O',
18
- difficulty: difficulty // easy, medium, hard
19
- };
20
- }
21
-
22
- // Check for winner in tic-tac-toe
23
- function checkWinner(board) {
24
- const lines = [
25
- [0, 1, 2], [3, 4, 5], [6, 7, 8], // rows
26
- [0, 3, 6], [1, 4, 7], [2, 5, 8], // cols
27
- [0, 4, 8], [2, 4, 6] // diagonals
28
- ];
29
-
30
- for (const [a, b, c] of lines) {
31
- if (board[a] && board[a] === board[b] && board[a] === board[c]) {
32
- return board[a];
33
- }
34
- }
35
-
36
- return null;
37
- }
38
-
39
- // Make a move
40
- function makeMove(gameState, position, playerSymbol) {
41
- const { board, moves, winner, gameOver } = gameState;
42
-
43
- // Validate move
44
- if (gameOver) {
45
- return { error: 'Game is already over' };
46
- }
47
-
48
- if (position < 1 || position > 9) {
49
- return { error: 'Position must be 1-9' };
50
- }
51
-
52
- const index = position - 1; // Convert to 0-based index
53
-
54
- if (board[index]) {
55
- return { error: 'Position already taken' };
56
- }
57
-
58
- // Make the move
59
- const newBoard = [...board];
60
- newBoard[index] = playerSymbol;
61
-
62
- const newMoves = moves + 1;
63
- const newWinner = checkWinner(newBoard);
64
- const newIsDraw = !newWinner && newBoard.every(cell => cell !== '');
65
- const newGameOver = newWinner || newIsDraw;
66
- const nextTurn = playerSymbol === 'X' ? 'O' : 'X';
67
-
68
- const newGameState = {
69
- ...gameState,
70
- board: newBoard,
71
- turn: newGameOver ? playerSymbol : nextTurn,
72
- moves: newMoves,
73
- winner: newWinner,
74
- gameOver: newGameOver,
75
- isDraw: newIsDraw,
76
- lastMove: { position, symbol: playerSymbol }
77
- };
78
-
79
- return { success: true, gameState: newGameState };
80
- }
81
-
82
- // Minimax algorithm for perfect AI play (hard difficulty)
83
- function minimax(board, depth, isMaximizing, aiSymbol, playerSymbol, alpha = -Infinity, beta = Infinity) {
84
- const winner = checkWinner(board);
85
-
86
- // Base cases
87
- if (winner === aiSymbol) return 10 - depth;
88
- if (winner === playerSymbol) return depth - 10;
89
- if (board.every(cell => cell !== '')) return 0; // Draw
90
-
91
- if (isMaximizing) {
92
- let maxEval = -Infinity;
93
- for (let i = 0; i < 9; i++) {
94
- if (board[i] === '') {
95
- board[i] = aiSymbol;
96
- const eval = minimax(board, depth + 1, false, aiSymbol, playerSymbol, alpha, beta);
97
- board[i] = '';
98
- maxEval = Math.max(maxEval, eval);
99
- alpha = Math.max(alpha, eval);
100
- if (beta <= alpha) break; // Alpha-beta pruning
101
- }
102
- }
103
- return maxEval;
104
- } else {
105
- let minEval = Infinity;
106
- for (let i = 0; i < 9; i++) {
107
- if (board[i] === '') {
108
- board[i] = playerSymbol;
109
- const eval = minimax(board, depth + 1, true, aiSymbol, playerSymbol, alpha, beta);
110
- board[i] = '';
111
- minEval = Math.min(minEval, eval);
112
- beta = Math.min(beta, eval);
113
- if (beta <= alpha) break; // Alpha-beta pruning
114
- }
115
- }
116
- return minEval;
117
- }
118
- }
119
-
120
- // Get best move using minimax (for hard difficulty)
121
- function getBestMove(board, aiSymbol, playerSymbol) {
122
- let bestMove = -1;
123
- let bestValue = -Infinity;
124
-
125
- for (let i = 0; i < 9; i++) {
126
- if (board[i] === '') {
127
- board[i] = aiSymbol;
128
- const moveValue = minimax(board, 0, false, aiSymbol, playerSymbol);
129
- board[i] = '';
130
-
131
- if (moveValue > bestValue) {
132
- bestMove = i + 1; // Convert to 1-based index
133
- bestValue = moveValue;
134
- }
135
- }
136
- }
137
-
138
- return bestMove;
139
- }
140
-
141
- // AI Strategy with difficulty levels
142
- function getAIMove(gameState, difficulty = 'medium') {
143
- const { board, aiSymbol, playerSymbol } = gameState;
144
- const availablePositions = getAvailablePositions(board);
145
-
146
- if (availablePositions.length === 0) {
147
- return null;
148
- }
149
-
150
- // Override difficulty if specified in gameState
151
- if (gameState.difficulty) {
152
- difficulty = gameState.difficulty;
153
- }
154
-
155
- switch (difficulty) {
156
- case 'easy':
157
- return getEasyAIMove(board, availablePositions, aiSymbol, playerSymbol);
158
- case 'medium':
159
- return getMediumAIMove(board, availablePositions, aiSymbol, playerSymbol);
160
- case 'hard':
161
- return getHardAIMove(board, aiSymbol, playerSymbol);
162
- default:
163
- return getMediumAIMove(board, availablePositions, aiSymbol, playerSymbol);
164
- }
165
- }
166
-
167
- // Easy AI: Mostly random with occasional smart moves
168
- function getEasyAIMove(board, availablePositions, aiSymbol, playerSymbol) {
169
- // 70% chance to play randomly
170
- if (Math.random() < 0.7) {
171
- const randomIndex = Math.floor(Math.random() * availablePositions.length);
172
- return availablePositions[randomIndex];
173
- }
174
-
175
- // 30% chance to make a smart move
176
- // 1. Try to win (15% chance)
177
- if (Math.random() < 0.5) {
178
- for (const pos of availablePositions) {
179
- const testBoard = [...board];
180
- testBoard[pos - 1] = aiSymbol;
181
- if (checkWinner(testBoard) === aiSymbol) {
182
- return pos;
183
- }
184
- }
185
- }
186
-
187
- // 2. Sometimes block player (15% chance)
188
- if (Math.random() < 0.5) {
189
- for (const pos of availablePositions) {
190
- const testBoard = [...board];
191
- testBoard[pos - 1] = playerSymbol;
192
- if (checkWinner(testBoard) === playerSymbol) {
193
- return pos;
194
- }
195
- }
196
- }
197
-
198
- // Fallback to random
199
- const randomIndex = Math.floor(Math.random() * availablePositions.length);
200
- return availablePositions[randomIndex];
201
- }
202
-
203
- // Medium AI: Balanced strategy with some randomness
204
- function getMediumAIMove(board, availablePositions, aiSymbol, playerSymbol) {
205
- // 1. Always try to win first
206
- for (const pos of availablePositions) {
207
- const testBoard = [...board];
208
- testBoard[pos - 1] = aiSymbol;
209
- if (checkWinner(testBoard) === aiSymbol) {
210
- return pos;
211
- }
212
- }
213
-
214
- // 2. Always block player from winning
215
- for (const pos of availablePositions) {
216
- const testBoard = [...board];
217
- testBoard[pos - 1] = playerSymbol;
218
- if (checkWinner(testBoard) === playerSymbol) {
219
- return pos;
220
- }
221
- }
222
-
223
- // 3. Add some randomness to avoid being too predictable (30% chance)
224
- if (Math.random() < 0.3) {
225
- const randomIndex = Math.floor(Math.random() * availablePositions.length);
226
- return availablePositions[randomIndex];
227
- }
228
-
229
- // 4. Take center if available
230
- if (availablePositions.includes(5)) {
231
- return 5;
232
- }
233
-
234
- // 5. Take corners
235
- const corners = [1, 3, 7, 9];
236
- const availableCorners = corners.filter(c => availablePositions.includes(c));
237
- if (availableCorners.length > 0) {
238
- const randomCorner = availableCorners[Math.floor(Math.random() * availableCorners.length)];
239
- return randomCorner;
240
- }
241
-
242
- // 6. Take any remaining position
243
- const randomIndex = Math.floor(Math.random() * availablePositions.length);
244
- return availablePositions[randomIndex];
245
- }
246
-
247
- // Hard AI: Perfect play using minimax algorithm
248
- function getHardAIMove(board, aiSymbol, playerSymbol) {
249
- return getBestMove(board, aiSymbol, playerSymbol);
250
- }
251
-
252
- // Make AI move
253
- function makeAIMove(gameState, difficulty = null) {
254
- if (gameState.gameOver || gameState.turn !== gameState.aiSymbol) {
255
- return { error: 'Not AI turn or game is over' };
256
- }
257
-
258
- const aiPosition = getAIMove(gameState, difficulty);
259
- if (!aiPosition) {
260
- return { error: 'No moves available' };
261
- }
262
-
263
- return makeMove(gameState, aiPosition, gameState.aiSymbol);
264
- }
265
-
266
- // Format tic-tac-toe board for display
267
- function formatTicTacToeDisplay(gameState) {
268
- const { board, moves, winner, isDraw, turn, lastMove, playerSymbol, aiSymbol, difficulty } = gameState;
269
-
270
- const difficultyEmoji = {
271
- 'easy': '😊',
272
- 'medium': '🤔',
273
- 'hard': '🧠'
274
- };
275
-
276
- const difficultyText = difficulty ? `${difficultyEmoji[difficulty] || '🤔'} ${difficulty.toUpperCase()}` : 'MEDIUM 🤔';
277
-
278
- let display = `🎯 **Tic-Tac-Toe vs AI** (${difficultyText}) - ${moves} moves\n\n`;
279
-
280
- // Create 3x3 grid display
281
- const symbols = board.map((cell, i) => cell || (i + 1).toString());
282
-
283
- display += '```\n';
284
- for (let row = 0; row < 3; row++) {
285
- const line = [];
286
- for (let col = 0; col < 3; col++) {
287
- const index = row * 3 + col;
288
- line.push(symbols[index]);
289
- }
290
- display += line.join(' │ ') + '\n';
291
- if (row < 2) display += '──┼───┼──\n';
292
- }
293
- display += '```\n\n';
294
-
295
- if (winner) {
296
- if (winner === playerSymbol) {
297
- display += `🎉 **You won!** Great job!`;
298
- } else {
299
- display += `🤖 **AI wins!** Better luck next time!`;
300
- }
301
- } else if (isDraw) {
302
- display += `🤝 **Draw!** Well played!`;
303
- } else {
304
- if (turn === playerSymbol) {
305
- display += `Your turn! Choose position 1-9`;
306
- } else {
307
- display += `AI is thinking... 🤔`;
308
- }
309
-
310
- if (lastMove) {
311
- const mover = lastMove.symbol === playerSymbol ? 'You' : 'AI';
312
- display += `\nLast move: ${mover} played ${lastMove.symbol} at position ${lastMove.position}`;
313
- }
314
- }
315
-
316
- return display;
317
- }
318
-
319
- // Get available positions
320
- function getAvailablePositions(board) {
321
- return board
322
- .map((cell, index) => cell === '' ? index + 1 : null)
323
- .filter(pos => pos !== null);
324
- }
325
-
326
- // Get difficulty description for users
327
- function getDifficultyDescription(difficulty) {
328
- const descriptions = {
329
- 'easy': '😊 **EASY**: AI plays mostly randomly, great for beginners!',
330
- 'medium': '🤔 **MEDIUM**: AI uses basic strategy but makes some mistakes.',
331
- 'hard': '🧠 **HARD**: AI plays perfectly, can you beat it?'
332
- };
333
- return descriptions[difficulty] || descriptions['medium'];
334
- }
335
-
336
- module.exports = {
337
- createInitialTicTacToeState,
338
- makeMove,
339
- makeAIMove,
340
- formatTicTacToeDisplay,
341
- checkWinner,
342
- getAvailablePositions,
343
- getAIMove,
344
- getDifficultyDescription
345
- };