slashvibe-mcp 0.2.9 → 0.3.13

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 (205) hide show
  1. package/README.md +1 -0
  2. package/analytics.js +107 -0
  3. package/auth-store.js +148 -0
  4. package/auto-update.js +130 -0
  5. package/bridges/bridge-monitor.js +388 -0
  6. package/bridges/discord-bot.js +431 -0
  7. package/bridges/farcaster.js +299 -0
  8. package/bridges/telegram.js +261 -0
  9. package/bridges/webhook-health.js +420 -0
  10. package/bridges/webhook-server.js +437 -0
  11. package/bridges/whatsapp.js +441 -0
  12. package/bridges/x-webhook.js +423 -0
  13. package/config.js +154 -5
  14. package/crypto.js +3 -8
  15. package/games/arcade.js +406 -0
  16. package/games/chess.js +451 -0
  17. package/games/colorguess.js +343 -0
  18. package/games/crossword-words.js +171 -0
  19. package/games/crossword.js +461 -0
  20. package/games/drawing.js +347 -0
  21. package/games/gameroulette.js +300 -0
  22. package/games/gamerouter.js +336 -0
  23. package/games/gamestatus.js +337 -0
  24. package/games/guessnumber.js +209 -0
  25. package/games/hangman.js +279 -0
  26. package/games/memory.js +338 -0
  27. package/games/multiplayer-tictactoe.js +389 -0
  28. package/games/pixelart.js +399 -0
  29. package/games/quickduel.js +354 -0
  30. package/games/riddle.js +371 -0
  31. package/games/rockpaperscissors.js +291 -0
  32. package/games/snake.js +406 -0
  33. package/games/storybuilder.js +343 -0
  34. package/games/tictactoe.js +345 -0
  35. package/games/twentyquestions.js +286 -0
  36. package/games/twotruths.js +207 -0
  37. package/games/werewolf.js +508 -0
  38. package/games/wordassociation.js +247 -0
  39. package/games/wordchain.js +135 -0
  40. package/index.js +105 -216
  41. package/intelligence/index.js +45 -0
  42. package/intelligence/infer.js +316 -0
  43. package/intelligence/interests.js +369 -0
  44. package/intelligence/patterns.js +651 -0
  45. package/intelligence/proactive.js +358 -0
  46. package/intelligence/serendipity.js +306 -0
  47. package/memory.js +166 -0
  48. package/notification-emitter.js +77 -0
  49. package/notify.js +102 -1
  50. package/package.json +21 -7
  51. package/prompts.js +1 -1
  52. package/protocol/index.js +161 -1
  53. package/setup.js +402 -0
  54. package/store/api.js +528 -82
  55. package/store/profiles.js +160 -12
  56. package/tools/_actions.js +463 -16
  57. package/tools/_deprecated/auto-suggest-connections.js +304 -0
  58. package/tools/_deprecated/bootstrap-skills.js +231 -0
  59. package/tools/_deprecated/bridge-dashboard.js +342 -0
  60. package/tools/_deprecated/bridge-health.js +400 -0
  61. package/tools/_deprecated/bridge-live.js +384 -0
  62. package/tools/_deprecated/bridges.js +383 -0
  63. package/tools/_deprecated/colorguess.js +281 -0
  64. package/tools/_deprecated/discover-insights.js +379 -0
  65. package/tools/_deprecated/discover-momentum.js +256 -0
  66. package/tools/_deprecated/discovery-analytics.js +345 -0
  67. package/tools/_deprecated/discovery-auto-suggest.js +275 -0
  68. package/tools/_deprecated/discovery-bootstrap.js +267 -0
  69. package/tools/_deprecated/discovery-daily.js +375 -0
  70. package/tools/_deprecated/discovery-dashboard.js +385 -0
  71. package/tools/_deprecated/discovery-digest.js +314 -0
  72. package/tools/_deprecated/discovery-hub.js +357 -0
  73. package/tools/_deprecated/discovery-insights.js +384 -0
  74. package/tools/_deprecated/discovery-momentum.js +281 -0
  75. package/tools/_deprecated/discovery-monitor.js +319 -0
  76. package/tools/_deprecated/discovery-proactive.js +300 -0
  77. package/tools/_deprecated/draw.js +317 -0
  78. package/tools/_deprecated/farcaster.js +307 -0
  79. package/tools/_deprecated/forget.js +119 -0
  80. package/tools/_deprecated/games-catalog.js +376 -0
  81. package/tools/_deprecated/games.js +313 -0
  82. package/tools/_deprecated/guessnumber.js +194 -0
  83. package/tools/_deprecated/hangman.js +129 -0
  84. package/tools/_deprecated/multiplayer-tictactoe.js +303 -0
  85. package/tools/_deprecated/recall.js +147 -0
  86. package/tools/_deprecated/remember.js +86 -0
  87. package/tools/_deprecated/riddle.js +240 -0
  88. package/tools/_deprecated/run-bootstrap.js +69 -0
  89. package/tools/_deprecated/skills-analytics.js +349 -0
  90. package/tools/_deprecated/skills-bootstrap.js +301 -0
  91. package/tools/_deprecated/skills-dashboard.js +268 -0
  92. package/tools/_deprecated/skills.js +380 -0
  93. package/tools/_deprecated/smart-intro.js +353 -0
  94. package/tools/_deprecated/storybuilder.js +331 -0
  95. package/tools/_deprecated/telegram-bot.js +183 -0
  96. package/tools/_deprecated/telegram-setup.js +214 -0
  97. package/tools/_deprecated/twentyquestions.js +143 -0
  98. package/tools/_discovery-enhanced.js +290 -0
  99. package/tools/_discovery.js +439 -0
  100. package/tools/_proactive-discovery.js +301 -0
  101. package/tools/_shared/index.js +64 -0
  102. package/tools/_shared.js +234 -0
  103. package/tools/_work-context.js +338 -0
  104. package/tools/_work-context.manual-test.js +199 -0
  105. package/tools/_work-context.test.js +260 -0
  106. package/tools/activity.js +220 -0
  107. package/tools/admin-inbox.js +218 -0
  108. package/tools/agent-treasury.js +288 -0
  109. package/tools/analytics.js +191 -0
  110. package/tools/approve.js +197 -0
  111. package/tools/arcade.js +173 -0
  112. package/tools/artifact-create.js +17 -11
  113. package/tools/artifact-view.js +31 -1
  114. package/tools/artifacts-price.js +47 -43
  115. package/tools/ask-expert.js +160 -0
  116. package/tools/available.js +120 -0
  117. package/tools/become-expert.js +150 -0
  118. package/tools/broadcast.js +286 -0
  119. package/tools/chat.js +202 -0
  120. package/tools/collaborative-drawing.js +286 -0
  121. package/tools/connection-status.js +178 -0
  122. package/tools/crossword.js +17 -1
  123. package/tools/discover.js +350 -34
  124. package/tools/dm.js +115 -73
  125. package/tools/drawing.js +1 -1
  126. package/tools/earnings.js +126 -0
  127. package/tools/echo.js +16 -0
  128. package/tools/feed.js +51 -7
  129. package/tools/follow.js +224 -0
  130. package/tools/friends.js +207 -0
  131. package/tools/game.js +2 -2
  132. package/tools/genesis.js +233 -0
  133. package/tools/gig-browse.js +206 -0
  134. package/tools/gig-complete.js +144 -0
  135. package/tools/handoff.js +7 -1
  136. package/tools/help.js +4 -4
  137. package/tools/idea.js +9 -2
  138. package/tools/inbox.js +334 -28
  139. package/tools/init.js +727 -36
  140. package/tools/invite.js +15 -4
  141. package/tools/l2-bridge.js +272 -0
  142. package/tools/l2-status.js +217 -0
  143. package/tools/l2.js +206 -0
  144. package/tools/migrate.js +156 -0
  145. package/tools/mint.js +377 -0
  146. package/tools/multiplayer-game.js +1 -1
  147. package/tools/notifications.js +415 -0
  148. package/tools/observe.js +200 -0
  149. package/tools/onboarding.js +147 -0
  150. package/tools/open.js +143 -12
  151. package/tools/party-game.js +2 -2
  152. package/tools/plan.js +225 -0
  153. package/tools/presence-agent.js +7 -0
  154. package/tools/proof-of-work.js +100 -104
  155. package/tools/pulse.js +218 -0
  156. package/tools/reply.js +166 -0
  157. package/tools/report.js +2 -2
  158. package/tools/reputation.js +175 -0
  159. package/tools/request.js +17 -3
  160. package/tools/schedule.js +367 -0
  161. package/tools/search-messages.js +123 -0
  162. package/tools/session.js +420 -0
  163. package/tools/session_price.js +128 -0
  164. package/tools/settings.js +126 -3
  165. package/tools/ship.js +31 -8
  166. package/tools/shipback.js +326 -0
  167. package/tools/smart-check.js +201 -0
  168. package/tools/social-processor.js +445 -0
  169. package/tools/solo-game.js +1 -1
  170. package/tools/start.js +335 -93
  171. package/tools/status.js +53 -6
  172. package/tools/stuck.js +297 -0
  173. package/tools/subscribe.js +148 -0
  174. package/tools/subscriptions.js +134 -0
  175. package/tools/suggest-tags.js +6 -8
  176. package/tools/tag-suggestions.js +257 -0
  177. package/tools/tip.js +193 -0
  178. package/tools/token.js +103 -0
  179. package/tools/update.js +1 -1
  180. package/tools/wallet.js +239 -186
  181. package/tools/watch.js +157 -0
  182. package/tools/webhook-test.js +388 -0
  183. package/tools/who.js +54 -3
  184. package/tools/withdraw.js +145 -0
  185. package/tools/work-summary.js +96 -0
  186. package/tools/workshop.js +327 -0
  187. package/tools/x-mentions.js +1 -1
  188. package/version.json +14 -3
  189. package/tools/artifacts-buy.js +0 -111
  190. package/tools/connect.js +0 -284
  191. package/tools/gigs-apply.js +0 -99
  192. package/tools/gigs-browse.js +0 -114
  193. package/tools/gigs-complete.js +0 -128
  194. package/tools/gigs-post.js +0 -140
  195. package/tools/live-off.js +0 -109
  196. package/tools/live-watch.js +0 -176
  197. package/tools/live.js +0 -128
  198. package/tools/sessions-browse.js +0 -105
  199. package/tools/whats-happening.js +0 -125
  200. /package/tools/{away.js → _deprecated/away.js} +0 -0
  201. /package/tools/{back.js → _deprecated/back.js} +0 -0
  202. /package/tools/{mute.js → _deprecated/mute.js} +0 -0
  203. /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
  204. /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
  205. /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
@@ -0,0 +1,313 @@
1
+ /**
2
+ * vibe games — Browse and launch workshop games
3
+ *
4
+ * Discover all available games, get recommendations, and launch games
5
+ */
6
+
7
+ const config = require('../config');
8
+ const store = require('../store');
9
+ const { createGamePayload, formatPayload } = require('../protocol');
10
+ const { requireInit, normalizeHandle } = require('./_shared');
11
+
12
+ // Import game systems
13
+ const arcade = require('../games/arcade');
14
+ const gamerouter = require('../games/gamerouter');
15
+
16
+ // Track active game sessions per user
17
+ const activeSessions = new Map();
18
+
19
+ const definition = {
20
+ name: 'vibe_games',
21
+ description: 'Browse and launch workshop games. Discover new games, get recommendations, or launch any game.',
22
+ inputSchema: {
23
+ type: 'object',
24
+ properties: {
25
+ action: {
26
+ type: 'string',
27
+ description: 'What to do: browse, launch, suggest, help, status',
28
+ enum: ['browse', 'launch', 'suggest', 'help', 'status', 'quit']
29
+ },
30
+ game: {
31
+ type: 'string',
32
+ description: 'Game to launch (e.g., hangman, wordchain, drawing)'
33
+ },
34
+ category: {
35
+ type: 'string',
36
+ description: 'Game category to browse (classic, word, puzzle, action, creative, social)'
37
+ },
38
+ difficulty: {
39
+ type: 'string',
40
+ description: 'Difficulty preference for suggestions (easy, medium, hard)',
41
+ enum: ['easy', 'medium', 'hard']
42
+ },
43
+ players: {
44
+ type: 'string',
45
+ description: 'Player preference for suggestions (solo, multiplayer)',
46
+ enum: ['solo', 'multiplayer']
47
+ },
48
+ command: {
49
+ type: 'string',
50
+ description: 'Command to send to active game'
51
+ }
52
+ },
53
+ required: []
54
+ }
55
+ };
56
+
57
+ // Get user session
58
+ function getUserSession(handle) {
59
+ if (!activeSessions.has(handle)) {
60
+ activeSessions.set(handle, {
61
+ currentGame: null,
62
+ gameState: null,
63
+ arcadeState: arcade.createInitialArcadeState(),
64
+ lastActivity: new Date().toISOString()
65
+ });
66
+ }
67
+ return activeSessions.get(handle);
68
+ }
69
+
70
+ // Clear user session
71
+ function clearUserSession(handle) {
72
+ activeSessions.delete(handle);
73
+ }
74
+
75
+ // Format game list for display
76
+ function formatGameList(games, title = 'Games') {
77
+ if (!games || games.length === 0) {
78
+ return `**${title}**\n*No games found.*`;
79
+ }
80
+
81
+ let display = `**${title}** (${games.length} games)\n\n`;
82
+
83
+ games.forEach(game => {
84
+ display += `${game.icon || 'šŸŽ®'} **${game.name}**\n`;
85
+ display += ` *${game.description}*\n`;
86
+ display += ` Players: ${game.players} • Difficulty: ${game.difficulty}\n\n`;
87
+ });
88
+
89
+ return display;
90
+ }
91
+
92
+ // Launch a specific game
93
+ function launchGame(session, gameId) {
94
+ // Check if game exists
95
+ if (!gamerouter.gameAvailable(gameId)) {
96
+ const availableGames = gamerouter.getAvailableGames();
97
+ return {
98
+ error: `Game '${gameId}' not found! Available games: ${availableGames.join(', ')}`
99
+ };
100
+ }
101
+
102
+ // Create game state
103
+ const result = gamerouter.createGameState(gameId);
104
+ if (result.error) {
105
+ return { error: result.error };
106
+ }
107
+
108
+ // Update session
109
+ session.currentGame = gameId;
110
+ session.gameState = result.gameState;
111
+ session.lastActivity = new Date().toISOString();
112
+
113
+ // Get display
114
+ const display = gamerouter.displayGame(gameId, result.gameState);
115
+ const instructions = gamerouter.getQuickStart(gameId);
116
+
117
+ return {
118
+ success: true,
119
+ display: display + '\n\nšŸ’” **Quick Start:** ' + instructions + '\n\n*Use `vibe games --command [action]` to play, or `vibe games --action quit` to exit.*'
120
+ };
121
+ }
122
+
123
+ // Handle game command
124
+ function handleGameCommand(session, command) {
125
+ if (!session.currentGame || !session.gameState) {
126
+ return { error: 'No active game. Use `vibe games --action launch --game [name]` to start a game.' };
127
+ }
128
+
129
+ const result = gamerouter.handleGameCommand(session.currentGame, session.gameState, command);
130
+ if (result.error) {
131
+ return { error: result.error };
132
+ }
133
+
134
+ // Update session
135
+ if (result.gameState) {
136
+ session.gameState = result.gameState;
137
+ session.lastActivity = new Date().toISOString();
138
+ }
139
+
140
+ // Format response
141
+ let display = '';
142
+ if (result.display) {
143
+ display = result.display;
144
+ } else if (result.gameState) {
145
+ display = gamerouter.displayGame(session.currentGame, result.gameState);
146
+ } else {
147
+ display = 'Command processed.';
148
+ }
149
+
150
+ return { success: true, display: display };
151
+ }
152
+
153
+ // Generate game recommendations
154
+ function getRecommendations(difficulty = 'any', players = 'any') {
155
+ const suggestions = gamerouter.getGameSuggestions(players, difficulty);
156
+
157
+ let display = 'šŸŽÆ **Game Recommendations**\n\n';
158
+
159
+ if (difficulty !== 'any') {
160
+ display += `*${difficulty.charAt(0).toUpperCase() + difficulty.slice(1)} difficulty games*\n`;
161
+ }
162
+ if (players !== 'any') {
163
+ display += `*${players.charAt(0).toUpperCase() + players.slice(1)} games*\n`;
164
+ }
165
+
166
+ display += '\n';
167
+
168
+ const recommendedGames = suggestions.slice(0, 6); // Show top 6 recommendations
169
+
170
+ recommendedGames.forEach(game => {
171
+ const status = game.available ? 'āœ…' : '🚧';
172
+ display += `${status} ${game.icon || 'šŸŽ®'} **${game.name}** (${game.players})\n`;
173
+ display += ` *${game.description}*\n`;
174
+ display += ` Launch with: \`vibe games --action launch --game ${game.id}\`\n\n`;
175
+ });
176
+
177
+ if (suggestions.length > 6) {
178
+ display += `*... and ${suggestions.length - 6} more games! Use \`vibe games --action browse\` to see all.*\n`;
179
+ }
180
+
181
+ return display;
182
+ }
183
+
184
+ // Generate help text
185
+ function generateHelp() {
186
+ return `šŸŽ® **Workshop Games Help**
187
+
188
+ **Actions:**
189
+ • \`vibe games --action browse\` - Browse all available games
190
+ • \`vibe games --action browse --category word\` - Browse word games
191
+ • \`vibe games --action launch --game hangman\` - Launch hangman game
192
+ • \`vibe games --action suggest\` - Get game recommendations
193
+ • \`vibe games --action suggest --difficulty easy --players solo\` - Filtered suggestions
194
+ • \`vibe games --action status\` - See your current game status
195
+ • \`vibe games --action quit\` - Quit current game
196
+
197
+ **Game Categories:**
198
+ šŸŽ² classic • šŸ“ word • 🧩 puzzle • ⚔ action • šŸŽØ creative • šŸ‘„ social
199
+
200
+ **While Playing:**
201
+ • \`vibe games --command [action]\` - Send command to current game
202
+ • Each game has its own commands - check the quick start guide!
203
+
204
+ **Popular Games:**
205
+ ā­• tic-tac-toe • šŸŽÆ hangman • šŸ”— wordchain • šŸŽØ drawing • ā“ twentyquestions
206
+
207
+ *Type \`vibe games --action browse\` to see all ${gamerouter.getAvailableGames().length} available games!*`;
208
+ }
209
+
210
+ async function handler(args) {
211
+ const initCheck = requireInit();
212
+ if (initCheck) return initCheck;
213
+
214
+ const { action = 'browse', game, category, difficulty, players, command } = args;
215
+ const myHandle = config.getHandle();
216
+ const session = getUserSession(myHandle);
217
+
218
+ try {
219
+ switch (action) {
220
+ case 'help':
221
+ return { display: generateHelp() };
222
+
223
+ case 'browse': {
224
+ if (category) {
225
+ // Browse specific category using arcade
226
+ const result = arcade.navigateToCategory(session.arcadeState, category);
227
+ if (result.error) {
228
+ return { display: result.error };
229
+ }
230
+ session.arcadeState = result.gameState;
231
+ const display = arcade.formatArcadeDisplay(session.arcadeState);
232
+ return { display };
233
+ } else {
234
+ // Browse all games using arcade
235
+ session.arcadeState = arcade.createInitialArcadeState();
236
+ const display = arcade.formatArcadeDisplay(session.arcadeState);
237
+ return { display };
238
+ }
239
+ }
240
+
241
+ case 'launch': {
242
+ if (!game) {
243
+ return { display: 'Please specify a game to launch. Use `vibe games --action browse` to see available games.' };
244
+ }
245
+ return launchGame(session, game);
246
+ }
247
+
248
+ case 'suggest': {
249
+ const display = getRecommendations(difficulty, players);
250
+ return { display };
251
+ }
252
+
253
+ case 'status': {
254
+ if (session.currentGame) {
255
+ const display = gamerouter.displayGame(session.currentGame, session.gameState);
256
+ return {
257
+ display: `**Current Game:** ${session.currentGame}\n\n${display}\n\n*Use \`vibe games --command [action]\` to play, or \`vibe games --action quit\` to exit.*`
258
+ };
259
+ } else {
260
+ const totalGames = gamerouter.getAvailableGames().length;
261
+ return {
262
+ display: `**No Active Game**\n\nšŸŽ® **${totalGames} games available** in the Workshop\n\nUse \`vibe games --action launch --game [name]\` to start a game\nOr \`vibe games --action browse\` to explore all games`
263
+ };
264
+ }
265
+ }
266
+
267
+ case 'quit': {
268
+ if (session.currentGame) {
269
+ const gameName = session.currentGame;
270
+ session.currentGame = null;
271
+ session.gameState = null;
272
+ return { display: `āœ… Quit ${gameName}. Use \`vibe games --action browse\` to find another game!` };
273
+ } else {
274
+ return { display: 'No active game to quit.' };
275
+ }
276
+ }
277
+
278
+ default: {
279
+ // If we have a command and active game, handle it
280
+ if (command && session.currentGame) {
281
+ return handleGameCommand(session, command);
282
+ }
283
+
284
+ // Default to browse if no specific action
285
+ session.arcadeState = arcade.createInitialArcadeState();
286
+ const display = arcade.formatArcadeDisplay(session.arcadeState);
287
+ return { display };
288
+ }
289
+ }
290
+
291
+ } catch (error) {
292
+ console.error('[games] Error:', error);
293
+ return { display: `Error: ${error.message}. Try \`vibe games --action help\` for usage.` };
294
+ }
295
+ }
296
+
297
+ // Clean up inactive sessions (called periodically)
298
+ function cleanupInactiveSessions() {
299
+ const now = Date.now();
300
+ const TIMEOUT = 30 * 60 * 1000; // 30 minutes
301
+
302
+ for (const [handle, session] of activeSessions.entries()) {
303
+ const lastActivity = new Date(session.lastActivity).getTime();
304
+ if (now - lastActivity > TIMEOUT) {
305
+ activeSessions.delete(handle);
306
+ }
307
+ }
308
+ }
309
+
310
+ // Run cleanup every 10 minutes
311
+ setInterval(cleanupInactiveSessions, 10 * 60 * 1000);
312
+
313
+ module.exports = { definition, handler };
@@ -0,0 +1,194 @@
1
+ /**
2
+ * vibe guessnumber — Play guess the number game with someone
3
+ *
4
+ * A fun number guessing game with difficulty levels
5
+ */
6
+
7
+ const config = require('../config');
8
+ const store = require('../store');
9
+ const { createGamePayload } = require('../protocol');
10
+ const { requireInit, normalizeHandle } = require('./_shared');
11
+
12
+ // Import game implementation
13
+ const guessNumber = require('../games/guessnumber');
14
+
15
+ // Post game results to board and Discord
16
+ async function postGameResult(winner, loser, moves, difficulty) {
17
+ const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
18
+
19
+ // Post to board
20
+ try {
21
+ const content = `@${winner} guessed the number in ${moves} moves! (${difficulty} difficulty) vs @${loser}`;
22
+
23
+ await fetch(`${API_URL}/api/board`, {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify({
27
+ author: 'echo',
28
+ content,
29
+ category: 'general'
30
+ })
31
+ });
32
+ } catch (e) {
33
+ console.error('[guessnumber] Failed to post to board:', e.message);
34
+ }
35
+
36
+ // Post to Discord
37
+ try {
38
+ await fetch(`${API_URL}/api/discord-bridge`, {
39
+ method: 'POST',
40
+ headers: { 'Content-Type': 'application/json' },
41
+ body: JSON.stringify({
42
+ type: 'game',
43
+ data: {
44
+ game: 'guess-the-number',
45
+ winner: winner,
46
+ loser: loser,
47
+ player1: winner,
48
+ player2: loser,
49
+ moves: moves,
50
+ difficulty: difficulty,
51
+ draw: false
52
+ }
53
+ })
54
+ });
55
+ } catch (e) {
56
+ console.error('[guessnumber] Failed to post to Discord:', e.message);
57
+ }
58
+ }
59
+
60
+ const definition = {
61
+ name: 'vibe_guessnumber',
62
+ description: 'Play a guess the number game with someone. Choose difficulty: easy (1-10), medium (1-50), hard (1-100), extreme (1-1000)',
63
+ inputSchema: {
64
+ type: 'object',
65
+ properties: {
66
+ handle: {
67
+ type: 'string',
68
+ description: 'Who to play with (e.g., @solienne)'
69
+ },
70
+ difficulty: {
71
+ type: 'string',
72
+ description: 'Game difficulty (default: medium)',
73
+ enum: ['easy', 'medium', 'hard', 'extreme']
74
+ },
75
+ guess: {
76
+ type: ['number', 'string'],
77
+ description: 'Your number guess'
78
+ }
79
+ },
80
+ required: ['handle']
81
+ }
82
+ };
83
+
84
+ /**
85
+ * Parse game state from thread
86
+ */
87
+ function getGameState(thread) {
88
+ // Find the most recent guess number game payload
89
+ for (let i = thread.length - 1; i >= 0; i--) {
90
+ const msg = thread[i];
91
+ if (msg.payload?.type === 'game' && msg.payload?.game === 'guessnumber') {
92
+ return msg.payload.state;
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+
98
+ /**
99
+ * Format game display
100
+ */
101
+ function formatGameDisplay(gameState, them) {
102
+ const display = guessNumber.formatGuessNumberDisplay(gameState);
103
+
104
+ if (gameState.gameOver && gameState.won) {
105
+ return `## Guess the Number with @${them}\n\n${display}\n\nGreat job! Start a new game anytime with \`vibe guessnumber @${them}\``;
106
+ } else if (gameState.gameOver && !gameState.won) {
107
+ return `## Guess the Number with @${them}\n\n${display}\n\nTry again! Start a new game with \`vibe guessnumber @${them}\``;
108
+ } else {
109
+ return `## Guess the Number with @${them}\n\n${display}\n\nMake your next guess with \`vibe guessnumber @${them} --guess NUMBER\``;
110
+ }
111
+ }
112
+
113
+ async function handler(args) {
114
+ const initCheck = requireInit();
115
+ if (initCheck) return initCheck;
116
+
117
+ const { handle, guess } = args;
118
+ const difficulty = args.difficulty || 'medium';
119
+ const myHandle = config.getHandle();
120
+ const them = normalizeHandle(handle);
121
+
122
+ if (them === myHandle) {
123
+ return { display: 'You can\'t play a guessing game with yourself! That would be cheating! šŸ˜„' };
124
+ }
125
+
126
+ // Get existing thread
127
+ const thread = await store.getThread(myHandle, them);
128
+ let gameState = getGameState(thread);
129
+
130
+ // Show current state if no guess provided
131
+ if (guess === undefined) {
132
+ if (!gameState) {
133
+ // Start new game
134
+ const newGameState = guessNumber.createInitialGuessNumberState(difficulty);
135
+ const payload = createGamePayload('guessnumber', newGameState);
136
+
137
+ const difficultyInfo = guessNumber.getDifficultyInfo()[difficulty];
138
+ await store.sendMessage(
139
+ myHandle,
140
+ them,
141
+ `Started a new guess the number game! (${difficulty}: ${difficultyInfo.range}) I'm thinking of a number... can you guess it?`,
142
+ 'dm',
143
+ payload
144
+ );
145
+
146
+ return {
147
+ display: `## New Guess the Number Game with @${them}\n\n${guessNumber.formatGuessNumberDisplay(newGameState)}\n\nUse \`vibe guessnumber @${them} --guess NUMBER\` to make your first guess!`
148
+ };
149
+ }
150
+
151
+ // Show existing game
152
+ return {
153
+ display: formatGameDisplay(gameState, them)
154
+ };
155
+ }
156
+
157
+ // Make a guess
158
+ // Initialize game if needed
159
+ if (!gameState) {
160
+ gameState = guessNumber.createInitialGuessNumberState(difficulty);
161
+ }
162
+
163
+ // Check if game is over
164
+ if (gameState.gameOver) {
165
+ return { display: `This game is over! Start a new game with \`vibe guessnumber @${them}\` (no guess).` };
166
+ }
167
+
168
+ // Make the guess
169
+ const result = guessNumber.makeGuess(gameState, guess);
170
+ if (result.error) {
171
+ return { display: `${result.error}` };
172
+ }
173
+
174
+ const newGameState = result.gameState;
175
+ const payload = createGamePayload('guessnumber', newGameState);
176
+
177
+ // Send message with game state
178
+ let message = '';
179
+ if (newGameState.won) {
180
+ message = `šŸŽ‰ You got it! The number was ${newGameState.targetNumber}! Solved in ${newGameState.moves} guesses!`;
181
+ // Post to board
182
+ await postGameResult(myHandle, them, newGameState.moves, newGameState.difficulty);
183
+ } else {
184
+ message = `Guessed ${guess}. ${newGameState.lastHint} Keep trying!`;
185
+ }
186
+
187
+ await store.sendMessage(myHandle, them, message, 'dm', payload);
188
+
189
+ return {
190
+ display: formatGameDisplay(newGameState, them)
191
+ };
192
+ }
193
+
194
+ module.exports = { definition, handler };
@@ -0,0 +1,129 @@
1
+ /**
2
+ * vibe hangman — Play hangman word guessing game
3
+ *
4
+ * Single-player game where you guess letters to find the hidden word
5
+ */
6
+
7
+ const config = require('../config');
8
+ const store = require('../store');
9
+ const { createGamePayload } = require('../protocol');
10
+ const { requireInit } = require('./_shared');
11
+
12
+ // Hangman game implementation
13
+ const hangman = require('../games/hangman');
14
+
15
+ const definition = {
16
+ name: 'vibe_hangman',
17
+ description: 'Play hangman - guess letters to find the hidden word',
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ guess: {
22
+ type: 'string',
23
+ description: 'Letter to guess (a-z) or "new" to start a new game'
24
+ },
25
+ difficulty: {
26
+ type: 'string',
27
+ description: 'Difficulty level for new games (easy, medium, hard)',
28
+ enum: ['easy', 'medium', 'hard']
29
+ }
30
+ },
31
+ required: []
32
+ }
33
+ };
34
+
35
+ /**
36
+ * Get latest hangman game state for user
37
+ */
38
+ async function getLatestHangmanState(handle) {
39
+ // Get user's thread with themselves (for single-player games)
40
+ const thread = await store.getThread(handle, handle);
41
+
42
+ // Find the most recent hangman game
43
+ for (let i = thread.length - 1; i >= 0; i--) {
44
+ const msg = thread[i];
45
+ if (msg.payload?.type === 'game' && msg.payload?.game === 'hangman') {
46
+ return msg.payload.state;
47
+ }
48
+ }
49
+ return null;
50
+ }
51
+
52
+ async function handler(args) {
53
+ const initCheck = requireInit();
54
+ if (initCheck) return initCheck;
55
+
56
+ const { guess, difficulty = 'medium' } = args;
57
+ const myHandle = config.getHandle();
58
+
59
+ // Get current game state
60
+ let gameState = await getLatestHangmanState(myHandle);
61
+
62
+ // Start new game
63
+ if (!gameState || guess === 'new' || (gameState.gameOver && !guess)) {
64
+ gameState = hangman.createInitialHangmanState(difficulty);
65
+ const payload = createGamePayload('hangman', gameState);
66
+
67
+ await store.sendMessage(myHandle, myHandle, `New ${difficulty} hangman game started!`, 'dm', payload);
68
+
69
+ const display = hangman.formatHangmanDisplay(gameState);
70
+ return {
71
+ display: `## New Hangman Game (${difficulty})\n\n${display}\n\nGuess letters with: \`vibe hangman --guess a\``
72
+ };
73
+ }
74
+
75
+ // Show current game if no guess
76
+ if (!guess) {
77
+ const display = hangman.formatHangmanDisplay(gameState);
78
+ const status = gameState.gameOver ?
79
+ '\nGame over! Use `vibe hangman` with no arguments to start a new game.' :
80
+ '\nGuess letters with: `vibe hangman --guess a`';
81
+
82
+ return {
83
+ display: `## Hangman Game\n\n${display}${status}`
84
+ };
85
+ }
86
+
87
+ // Make a guess
88
+ const result = hangman.makeGuess(gameState, guess);
89
+
90
+ if (result.error) {
91
+ const display = hangman.formatHangmanDisplay(gameState);
92
+ return {
93
+ display: `## Hangman Game\n\n${display}\n\nāŒ **${result.error}**\n\nTry again: \`vibe hangman --guess a\``
94
+ };
95
+ }
96
+
97
+ // Update game state
98
+ const newGameState = result.gameState;
99
+ const payload = createGamePayload('hangman', newGameState);
100
+
101
+ // Send message with updated state
102
+ let message = `Guessed "${guess.toUpperCase()}".`;
103
+ if (newGameState.gameOver) {
104
+ if (newGameState.won) {
105
+ message += ` šŸŽ‰ You won! The word was "${newGameState.word.toUpperCase()}".`;
106
+ } else {
107
+ message += ` šŸ’€ Game over! The word was "${newGameState.word.toUpperCase()}".`;
108
+ }
109
+ } else {
110
+ if (newGameState.correctGuesses.includes(guess.toLowerCase())) {
111
+ message += ' Good guess! āœ…';
112
+ } else {
113
+ message += ' Not in the word! āŒ';
114
+ }
115
+ }
116
+
117
+ await store.sendMessage(myHandle, myHandle, message, 'dm', payload);
118
+
119
+ const display = hangman.formatHangmanDisplay(newGameState);
120
+ const status = newGameState.gameOver ?
121
+ '\n\nšŸŽ® Use `vibe hangman` to start a new game!' :
122
+ '\n\nKeep guessing: `vibe hangman --guess e`';
123
+
124
+ return {
125
+ display: `## Hangman Game\n\n${display}${status}`
126
+ };
127
+ }
128
+
129
+ module.exports = { definition, handler };