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