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.
- package/LICENSE +21 -0
- package/README.md +58 -40
- package/config.js +171 -3
- package/index.js +136 -16
- package/intelligence/index.js +38 -0
- package/intelligence/infer.js +316 -0
- package/intelligence/patterns.js +651 -0
- package/intelligence/proactive.js +358 -0
- package/intelligence/serendipity.js +306 -0
- package/notify.js +141 -18
- package/package.json +8 -4
- package/presence.js +5 -1
- package/protocol/index.js +88 -1
- package/protocol/telegram-commands.js +199 -0
- package/store/api.js +360 -25
- package/store/index.js +7 -7
- package/store/local.js +67 -11
- package/store/profiles.js +287 -0
- package/store/reservations.js +321 -0
- package/store/skills.js +378 -0
- package/tools/_actions.js +270 -14
- package/tools/_connection-queue.js +257 -0
- package/tools/_discovery-enhanced.js +290 -0
- package/tools/_discovery.js +346 -0
- package/tools/_proactive-discovery.js +301 -0
- package/tools/admin-inbox.js +218 -0
- package/tools/agent-treasury.js +288 -0
- package/tools/agents.js +122 -0
- package/tools/arcade.js +173 -0
- package/tools/artifact-create.js +236 -0
- package/tools/artifact-view.js +174 -0
- package/tools/ask-expert.js +160 -0
- package/tools/auto-suggest-connections.js +304 -0
- package/tools/away.js +68 -0
- package/tools/back.js +51 -0
- package/tools/become-expert.js +150 -0
- package/tools/bootstrap-skills.js +231 -0
- package/tools/bridge-dashboard.js +342 -0
- package/tools/bridge-health.js +400 -0
- package/tools/bridge-live.js +384 -0
- package/tools/bridges.js +383 -0
- package/tools/bye.js +4 -0
- package/tools/collaborative-drawing.js +286 -0
- package/tools/colorguess.js +281 -0
- package/tools/crossword.js +369 -0
- package/tools/discover-insights.js +379 -0
- package/tools/discover-momentum.js +256 -0
- package/tools/discover.js +395 -0
- package/tools/discovery-analytics.js +345 -0
- package/tools/discovery-auto-suggest.js +275 -0
- package/tools/discovery-bootstrap.js +267 -0
- package/tools/discovery-daily.js +375 -0
- package/tools/discovery-dashboard.js +385 -0
- package/tools/discovery-digest.js +314 -0
- package/tools/discovery-hub.js +357 -0
- package/tools/discovery-insights.js +384 -0
- package/tools/discovery-momentum.js +281 -0
- package/tools/discovery-monitor.js +319 -0
- package/tools/discovery-proactive.js +300 -0
- package/tools/dm.js +62 -9
- package/tools/draw.js +317 -0
- package/tools/drawing.js +310 -0
- package/tools/echo.js +16 -0
- package/tools/farcaster.js +307 -0
- package/tools/feed.js +196 -0
- package/tools/game.js +218 -110
- package/tools/games-catalog.js +376 -0
- package/tools/games.js +313 -0
- package/tools/genesis.js +233 -0
- package/tools/guessnumber.js +194 -0
- package/tools/hangman.js +129 -0
- package/tools/help.js +269 -0
- package/tools/idea.js +210 -0
- package/tools/inbox.js +148 -25
- package/tools/init.js +651 -33
- package/tools/insights.js +123 -0
- package/tools/invite.js +142 -21
- package/tools/l2-bridge.js +272 -0
- package/tools/l2-status.js +217 -0
- package/tools/l2.js +206 -0
- package/tools/migrate.js +156 -0
- package/tools/mint.js +377 -0
- package/tools/multiplayer-game.js +275 -0
- package/tools/multiplayer-tictactoe.js +303 -0
- package/tools/mute.js +97 -0
- package/tools/notifications.js +415 -0
- package/tools/observe.js +200 -0
- package/tools/onboarding.js +147 -0
- package/tools/open.js +14 -2
- package/tools/party-game.js +314 -0
- package/tools/presence-agent.js +167 -0
- package/tools/profile.js +219 -0
- package/tools/pulse.js +218 -0
- package/tools/react.js +4 -0
- package/tools/release.js +83 -0
- package/tools/report.js +109 -0
- package/tools/reputation.js +175 -0
- package/tools/request.js +217 -0
- package/tools/reservations.js +116 -0
- package/tools/reserve.js +111 -0
- package/tools/riddle.js +240 -0
- package/tools/run-bootstrap.js +69 -0
- package/tools/settings.js +112 -0
- package/tools/ship.js +182 -0
- package/tools/shipback.js +326 -0
- package/tools/skills-analytics.js +349 -0
- package/tools/skills-bootstrap.js +301 -0
- package/tools/skills-dashboard.js +268 -0
- package/tools/skills-exchange.js +342 -0
- package/tools/skills.js +380 -0
- package/tools/smart-intro.js +353 -0
- package/tools/social-inbox.js +326 -69
- package/tools/social-post.js +251 -66
- package/tools/social-processor.js +445 -0
- package/tools/solo-game.js +390 -0
- package/tools/start.js +205 -83
- package/tools/storybuilder.js +331 -0
- package/tools/suggest-tags.js +186 -0
- package/tools/tag-suggestions.js +257 -0
- package/tools/telegram-bot.js +183 -0
- package/tools/telegram-setup.js +214 -0
- package/tools/tictactoe.js +155 -0
- package/tools/tip.js +120 -0
- package/tools/token.js +103 -0
- package/tools/twentyquestions.js +143 -0
- package/tools/wallet.js +127 -0
- package/tools/webhook-test.js +388 -0
- package/tools/who.js +118 -25
- package/tools/wordassociation.js +247 -0
- package/tools/workshop-buddy.js +394 -0
- package/tools/workshop.js +327 -0
- package/version.json +12 -3
- package/tools/board.js +0 -130
package/tools/genesis.js
ADDED
|
@@ -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 };
|
package/tools/hangman.js
ADDED
|
@@ -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 };
|