slashvibe-mcp 0.2.2 → 0.2.3

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -40
  3. package/config.js +171 -3
  4. package/index.js +136 -16
  5. package/intelligence/index.js +38 -0
  6. package/intelligence/infer.js +316 -0
  7. package/intelligence/patterns.js +651 -0
  8. package/intelligence/proactive.js +358 -0
  9. package/intelligence/serendipity.js +306 -0
  10. package/notify.js +141 -18
  11. package/package.json +8 -4
  12. package/presence.js +5 -1
  13. package/protocol/index.js +88 -1
  14. package/protocol/telegram-commands.js +199 -0
  15. package/store/api.js +360 -25
  16. package/store/index.js +7 -7
  17. package/store/local.js +67 -11
  18. package/store/profiles.js +287 -0
  19. package/store/reservations.js +321 -0
  20. package/store/skills.js +378 -0
  21. package/tools/_actions.js +270 -14
  22. package/tools/_connection-queue.js +257 -0
  23. package/tools/_discovery-enhanced.js +290 -0
  24. package/tools/_discovery.js +346 -0
  25. package/tools/_proactive-discovery.js +301 -0
  26. package/tools/admin-inbox.js +218 -0
  27. package/tools/agent-treasury.js +288 -0
  28. package/tools/agents.js +122 -0
  29. package/tools/arcade.js +173 -0
  30. package/tools/artifact-create.js +236 -0
  31. package/tools/artifact-view.js +174 -0
  32. package/tools/ask-expert.js +160 -0
  33. package/tools/auto-suggest-connections.js +304 -0
  34. package/tools/away.js +68 -0
  35. package/tools/back.js +51 -0
  36. package/tools/become-expert.js +150 -0
  37. package/tools/bootstrap-skills.js +231 -0
  38. package/tools/bridge-dashboard.js +342 -0
  39. package/tools/bridge-health.js +400 -0
  40. package/tools/bridge-live.js +384 -0
  41. package/tools/bridges.js +383 -0
  42. package/tools/bye.js +4 -0
  43. package/tools/collaborative-drawing.js +286 -0
  44. package/tools/colorguess.js +281 -0
  45. package/tools/crossword.js +369 -0
  46. package/tools/discover-insights.js +379 -0
  47. package/tools/discover-momentum.js +256 -0
  48. package/tools/discover.js +395 -0
  49. package/tools/discovery-analytics.js +345 -0
  50. package/tools/discovery-auto-suggest.js +275 -0
  51. package/tools/discovery-bootstrap.js +267 -0
  52. package/tools/discovery-daily.js +375 -0
  53. package/tools/discovery-dashboard.js +385 -0
  54. package/tools/discovery-digest.js +314 -0
  55. package/tools/discovery-hub.js +357 -0
  56. package/tools/discovery-insights.js +384 -0
  57. package/tools/discovery-momentum.js +281 -0
  58. package/tools/discovery-monitor.js +319 -0
  59. package/tools/discovery-proactive.js +300 -0
  60. package/tools/dm.js +62 -9
  61. package/tools/draw.js +317 -0
  62. package/tools/drawing.js +310 -0
  63. package/tools/echo.js +16 -0
  64. package/tools/farcaster.js +307 -0
  65. package/tools/feed.js +196 -0
  66. package/tools/game.js +218 -110
  67. package/tools/games-catalog.js +376 -0
  68. package/tools/games.js +313 -0
  69. package/tools/genesis.js +233 -0
  70. package/tools/guessnumber.js +194 -0
  71. package/tools/hangman.js +129 -0
  72. package/tools/help.js +269 -0
  73. package/tools/idea.js +210 -0
  74. package/tools/inbox.js +148 -25
  75. package/tools/init.js +651 -33
  76. package/tools/insights.js +123 -0
  77. package/tools/invite.js +142 -21
  78. package/tools/l2-bridge.js +272 -0
  79. package/tools/l2-status.js +217 -0
  80. package/tools/l2.js +206 -0
  81. package/tools/migrate.js +156 -0
  82. package/tools/mint.js +377 -0
  83. package/tools/multiplayer-game.js +275 -0
  84. package/tools/multiplayer-tictactoe.js +303 -0
  85. package/tools/mute.js +97 -0
  86. package/tools/notifications.js +415 -0
  87. package/tools/observe.js +200 -0
  88. package/tools/onboarding.js +147 -0
  89. package/tools/open.js +14 -2
  90. package/tools/party-game.js +314 -0
  91. package/tools/presence-agent.js +167 -0
  92. package/tools/profile.js +219 -0
  93. package/tools/pulse.js +218 -0
  94. package/tools/react.js +4 -0
  95. package/tools/release.js +83 -0
  96. package/tools/report.js +109 -0
  97. package/tools/reputation.js +175 -0
  98. package/tools/request.js +217 -0
  99. package/tools/reservations.js +116 -0
  100. package/tools/reserve.js +111 -0
  101. package/tools/riddle.js +240 -0
  102. package/tools/run-bootstrap.js +69 -0
  103. package/tools/settings.js +112 -0
  104. package/tools/ship.js +182 -0
  105. package/tools/shipback.js +326 -0
  106. package/tools/skills-analytics.js +349 -0
  107. package/tools/skills-bootstrap.js +301 -0
  108. package/tools/skills-dashboard.js +268 -0
  109. package/tools/skills-exchange.js +342 -0
  110. package/tools/skills.js +380 -0
  111. package/tools/smart-intro.js +353 -0
  112. package/tools/social-inbox.js +326 -69
  113. package/tools/social-post.js +251 -66
  114. package/tools/social-processor.js +445 -0
  115. package/tools/solo-game.js +390 -0
  116. package/tools/start.js +205 -83
  117. package/tools/storybuilder.js +331 -0
  118. package/tools/suggest-tags.js +186 -0
  119. package/tools/tag-suggestions.js +257 -0
  120. package/tools/telegram-bot.js +183 -0
  121. package/tools/telegram-setup.js +214 -0
  122. package/tools/tictactoe.js +155 -0
  123. package/tools/tip.js +120 -0
  124. package/tools/token.js +103 -0
  125. package/tools/twentyquestions.js +143 -0
  126. package/tools/wallet.js +127 -0
  127. package/tools/webhook-test.js +388 -0
  128. package/tools/who.js +118 -25
  129. package/tools/wordassociation.js +247 -0
  130. package/tools/workshop-buddy.js +394 -0
  131. package/tools/workshop.js +327 -0
  132. package/version.json +12 -3
  133. package/tools/board.js +0 -130
@@ -0,0 +1,233 @@
1
+ /**
2
+ * vibe_genesis - Genesis liquidity mining
3
+ *
4
+ * Deposit USDC to earn yield + Genesis multipliers
5
+ *
6
+ * Actions:
7
+ * - deposit: Lock USDC for yield farming
8
+ * - rewards: Check accumulated rewards
9
+ * - stats: View global Genesis stats
10
+ *
11
+ * Examples:
12
+ * - "vibe genesis deposit $100 for 90 days"
13
+ * - "check my genesis rewards"
14
+ * - "show genesis stats"
15
+ */
16
+
17
+ const fetch = require('node-fetch');
18
+
19
+ module.exports = {
20
+ name: 'vibe_genesis',
21
+ description: 'Genesis liquidity mining: deposit USDC to earn yield with early depositor bonuses. Base APY 5%, Genesis multipliers up to 2x, lock bonuses up to 2x.',
22
+
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: {
26
+ action: {
27
+ type: 'string',
28
+ enum: ['deposit', 'rewards', 'stats'],
29
+ description: 'Action to perform: deposit (add liquidity), rewards (check earnings), stats (global metrics)'
30
+ },
31
+ amount: {
32
+ type: 'number',
33
+ description: 'Amount to deposit in USD (min $10). Only for deposit action.'
34
+ },
35
+ lock_days: {
36
+ type: 'number',
37
+ enum: [0, 30, 90, 180],
38
+ description: 'Lock period in days: 0 (no lock), 30 (+20% APY), 90 (+50% APY), 180 (+100% APY). Only for deposit action.',
39
+ default: 0
40
+ }
41
+ },
42
+ required: ['action']
43
+ },
44
+
45
+ async execute({ action, amount, lock_days = 0 }, context) {
46
+ try {
47
+ const handle = context.handle;
48
+
49
+ if (!handle) {
50
+ return {
51
+ success: false,
52
+ error: 'Not authenticated. Use vibe init first.'
53
+ };
54
+ }
55
+
56
+ const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
57
+
58
+ switch (action) {
59
+ case 'deposit': {
60
+ if (!amount || amount < 10) {
61
+ return {
62
+ success: false,
63
+ error: 'Minimum deposit is $10'
64
+ };
65
+ }
66
+
67
+ const response = await fetch(`${apiUrl}/api/genesis/deposit`, {
68
+ method: 'POST',
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ 'Authorization': `Bearer ${context.token}`
72
+ },
73
+ body: JSON.stringify({
74
+ from: handle,
75
+ amount,
76
+ lock_days
77
+ })
78
+ });
79
+
80
+ const result = await response.json();
81
+
82
+ if (!response.ok) {
83
+ return {
84
+ success: false,
85
+ error: result.error || 'Deposit failed',
86
+ details: result.details
87
+ };
88
+ }
89
+
90
+ const effectiveAPY = result.effective_apy * 100;
91
+ const unlockDate = result.unlock_at
92
+ ? new Date(result.unlock_at).toLocaleDateString()
93
+ : 'No lock';
94
+
95
+ return {
96
+ success: true,
97
+ message: `Deposited $${amount} to Genesis mining`,
98
+ deposit: {
99
+ deposit_id: result.deposit_id,
100
+ amount: `$${amount}`,
101
+ base_apy: `${(result.base_apy * 100).toFixed(2)}%`,
102
+ genesis_multiplier: `${result.genesis_multiplier}x`,
103
+ effective_apy: `${effectiveAPY.toFixed(2)}%`,
104
+ lock_days,
105
+ unlock_at: unlockDate,
106
+ current_tvl: `$${result.current_tvl.toFixed(2)}`
107
+ },
108
+ formatted: `
109
+ šŸ’§ Genesis Deposit Successful!
110
+
111
+ Amount: $${amount}
112
+ Lock Period: ${lock_days} days
113
+ Unlock: ${unlockDate}
114
+
115
+ APY Breakdown:
116
+ Base: ${(result.base_apy * 100).toFixed(2)}%
117
+ Genesis Multiplier: ${result.genesis_multiplier}x
118
+ Lock Bonus: ${lock_days >= 180 ? '2.0x' : lock_days >= 90 ? '1.5x' : lock_days >= 30 ? '1.2x' : '1.0x'}
119
+
120
+ Effective APY: ${effectiveAPY.toFixed(2)}%
121
+
122
+ Current TVL: $${result.current_tvl.toFixed(2)}
123
+
124
+ Rewards accrue daily. Check with: vibe genesis rewards
125
+ `.trim()
126
+ };
127
+ }
128
+
129
+ case 'rewards': {
130
+ const response = await fetch(
131
+ `${apiUrl}/api/genesis/rewards?handle=${handle}`,
132
+ {
133
+ headers: {
134
+ 'Authorization': `Bearer ${context.token}`
135
+ }
136
+ }
137
+ );
138
+
139
+ const result = await response.json();
140
+
141
+ if (!response.ok) {
142
+ return {
143
+ success: false,
144
+ error: result.error || 'Failed to fetch rewards',
145
+ message: result.message
146
+ };
147
+ }
148
+
149
+ const deposits = result.deposits || [];
150
+ const totals = result.totals;
151
+
152
+ const depositLines = deposits.map(d => {
153
+ return ` $${d.amount.toFixed(2)} | ${d.days_deposited}d | ${(d.effective_apy * 100).toFixed(2)}% APY | +$${d.unclaimed_rewards.toFixed(2)} earned`;
154
+ });
155
+
156
+ return {
157
+ success: true,
158
+ rewards: totals,
159
+ deposits: deposits,
160
+ formatted: `
161
+ šŸ’° Genesis Rewards
162
+
163
+ Total Deposited: $${totals.total_deposited.toFixed(2)}
164
+ Unclaimed Rewards: $${totals.total_rewards.toFixed(2)}
165
+ Total Value: $${totals.total_value.toFixed(2)}
166
+
167
+ Active Deposits:
168
+ ${depositLines.length > 0 ? depositLines.join('\n') : ' No active deposits'}
169
+
170
+ Rewards accrue daily based on your effective APY.
171
+ `.trim()
172
+ };
173
+ }
174
+
175
+ case 'stats': {
176
+ const response = await fetch(`${apiUrl}/api/genesis/stats`);
177
+ const result = await response.json();
178
+
179
+ if (!response.ok) {
180
+ return {
181
+ success: false,
182
+ error: 'Failed to fetch stats'
183
+ };
184
+ }
185
+
186
+ const tierLines = result.multiplier_tiers?.map(t => {
187
+ const marker = t.status === 'current' ? '→' : t.status === 'passed' ? 'āœ“' : ' ';
188
+ return ` ${marker} ${t.tvl_range.padEnd(12)} ${t.multiplier}x ${t.status}`;
189
+ }) || [];
190
+
191
+ return {
192
+ success: true,
193
+ stats: result,
194
+ formatted: `
195
+ 🌊 Genesis Liquidity Mining Stats
196
+
197
+ Phase: ${result.genesis_phase} (${result.genesis_progress}% to goal)
198
+ Total Value Locked: $${result.tvl.toFixed(2)}
199
+ Depositors: ${result.total_depositors}
200
+ Avg Deposit: $${result.avg_deposit.toFixed(2)}
201
+
202
+ Current Multiplier: ${result.current_multiplier}x
203
+ Base APY: ${(result.base_apy * 100).toFixed(2)}%
204
+
205
+ Multiplier Tiers:
206
+ ${tierLines.join('\n')}
207
+
208
+ Rewards Distributed: $${result.total_rewards_distributed.toFixed(2)}
209
+
210
+ Top Depositors:
211
+ ${result.top_depositors?.slice(0, 5).map((d, i) =>
212
+ ` ${i + 1}. ${d.handle} - $${d.total_deposited.toFixed(2)}`
213
+ ).join('\n') || ' No depositors yet'}
214
+ `.trim()
215
+ };
216
+ }
217
+
218
+ default:
219
+ return {
220
+ success: false,
221
+ error: 'Invalid action. Use: deposit, rewards, or stats'
222
+ };
223
+ }
224
+
225
+ } catch (error) {
226
+ return {
227
+ success: false,
228
+ error: 'Genesis action failed',
229
+ details: error.message
230
+ };
231
+ }
232
+ }
233
+ };
@@ -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 };