slashvibe-mcp 0.2.2 → 0.2.4

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 (162) hide show
  1. package/README.md +1 -0
  2. package/analytics.js +107 -0
  3. package/config.js +174 -3
  4. package/index.js +163 -34
  5. package/intelligence/index.js +45 -0
  6. package/intelligence/infer.js +316 -0
  7. package/intelligence/interests.js +369 -0
  8. package/intelligence/patterns.js +651 -0
  9. package/intelligence/proactive.js +358 -0
  10. package/intelligence/serendipity.js +306 -0
  11. package/notification-emitter.js +77 -0
  12. package/notify.js +141 -18
  13. package/package.json +14 -6
  14. package/presence.js +5 -1
  15. package/protocol/index.js +88 -1
  16. package/protocol/telegram-commands.js +199 -0
  17. package/store/api.js +469 -29
  18. package/store/index.js +7 -7
  19. package/store/local.js +67 -11
  20. package/store/profiles.js +435 -0
  21. package/store/reservations.js +321 -0
  22. package/store/skills.js +378 -0
  23. package/tools/_actions.js +491 -22
  24. package/tools/_connection-queue.js +257 -0
  25. package/tools/_discovery-enhanced.js +290 -0
  26. package/tools/_discovery.js +439 -0
  27. package/tools/_proactive-discovery.js +301 -0
  28. package/tools/_shared/index.js +64 -0
  29. package/tools/_work-context.js +338 -0
  30. package/tools/_work-context.manual-test.js +199 -0
  31. package/tools/_work-context.test.js +260 -0
  32. package/tools/admin-inbox.js +218 -0
  33. package/tools/agent-treasury.js +288 -0
  34. package/tools/agents.js +122 -0
  35. package/tools/analytics.js +191 -0
  36. package/tools/approve.js +197 -0
  37. package/tools/arcade.js +173 -0
  38. package/tools/artifact-create.js +247 -0
  39. package/tools/artifact-view.js +174 -0
  40. package/tools/artifacts-price.js +107 -0
  41. package/tools/ask-expert.js +160 -0
  42. package/tools/auto-suggest-connections.js +304 -0
  43. package/tools/away.js +68 -0
  44. package/tools/back.js +51 -0
  45. package/tools/become-expert.js +150 -0
  46. package/tools/bootstrap-skills.js +231 -0
  47. package/tools/bridge-dashboard.js +342 -0
  48. package/tools/bridge-health.js +400 -0
  49. package/tools/bridge-live.js +384 -0
  50. package/tools/bridges.js +383 -0
  51. package/tools/broadcast.js +286 -0
  52. package/tools/bye.js +4 -0
  53. package/tools/chat.js +202 -0
  54. package/tools/collaborative-drawing.js +286 -0
  55. package/tools/colorguess.js +281 -0
  56. package/tools/crossword.js +369 -0
  57. package/tools/discover-insights.js +379 -0
  58. package/tools/discover-momentum.js +256 -0
  59. package/tools/discover.js +675 -0
  60. package/tools/discovery-analytics.js +345 -0
  61. package/tools/discovery-auto-suggest.js +275 -0
  62. package/tools/discovery-bootstrap.js +267 -0
  63. package/tools/discovery-daily.js +375 -0
  64. package/tools/discovery-dashboard.js +385 -0
  65. package/tools/discovery-digest.js +314 -0
  66. package/tools/discovery-hub.js +357 -0
  67. package/tools/discovery-insights.js +384 -0
  68. package/tools/discovery-momentum.js +281 -0
  69. package/tools/discovery-monitor.js +319 -0
  70. package/tools/discovery-proactive.js +300 -0
  71. package/tools/dm.js +84 -14
  72. package/tools/draw.js +317 -0
  73. package/tools/drawing.js +310 -0
  74. package/tools/earnings.js +126 -0
  75. package/tools/echo.js +16 -0
  76. package/tools/farcaster.js +307 -0
  77. package/tools/feed.js +215 -0
  78. package/tools/follow.js +224 -0
  79. package/tools/friends.js +192 -0
  80. package/tools/game.js +218 -110
  81. package/tools/games-catalog.js +376 -0
  82. package/tools/games.js +313 -0
  83. package/tools/genesis.js +233 -0
  84. package/tools/gig-browse.js +206 -0
  85. package/tools/gig-complete.js +139 -0
  86. package/tools/guessnumber.js +194 -0
  87. package/tools/hangman.js +129 -0
  88. package/tools/help.js +269 -0
  89. package/tools/idea.js +217 -0
  90. package/tools/inbox.js +291 -25
  91. package/tools/init.js +657 -33
  92. package/tools/insights.js +123 -0
  93. package/tools/invite.js +142 -21
  94. package/tools/l2-bridge.js +272 -0
  95. package/tools/l2-status.js +217 -0
  96. package/tools/l2.js +206 -0
  97. package/tools/migrate.js +156 -0
  98. package/tools/mint.js +377 -0
  99. package/tools/multiplayer-game.js +275 -0
  100. package/tools/multiplayer-tictactoe.js +303 -0
  101. package/tools/mute.js +97 -0
  102. package/tools/notifications.js +415 -0
  103. package/tools/observe.js +200 -0
  104. package/tools/onboarding.js +147 -0
  105. package/tools/open.js +52 -3
  106. package/tools/party-game.js +314 -0
  107. package/tools/plan.js +225 -0
  108. package/tools/presence-agent.js +167 -0
  109. package/tools/profile.js +219 -0
  110. package/tools/proof-of-work.js +139 -0
  111. package/tools/pulse.js +218 -0
  112. package/tools/react.js +4 -0
  113. package/tools/release.js +83 -0
  114. package/tools/report.js +109 -0
  115. package/tools/reputation.js +175 -0
  116. package/tools/request.js +231 -0
  117. package/tools/reservations.js +116 -0
  118. package/tools/reserve.js +111 -0
  119. package/tools/riddle.js +240 -0
  120. package/tools/run-bootstrap.js +69 -0
  121. package/tools/schedule.js +367 -0
  122. package/tools/session.js +420 -0
  123. package/tools/session_price.js +128 -0
  124. package/tools/settings.js +200 -0
  125. package/tools/ship.js +188 -0
  126. package/tools/shipback.js +326 -0
  127. package/tools/skills-analytics.js +349 -0
  128. package/tools/skills-bootstrap.js +301 -0
  129. package/tools/skills-dashboard.js +268 -0
  130. package/tools/skills-exchange.js +342 -0
  131. package/tools/skills.js +380 -0
  132. package/tools/smart-intro.js +353 -0
  133. package/tools/social-inbox.js +326 -69
  134. package/tools/social-post.js +251 -66
  135. package/tools/social-processor.js +445 -0
  136. package/tools/solo-game.js +390 -0
  137. package/tools/start.js +296 -81
  138. package/tools/status.js +53 -6
  139. package/tools/storybuilder.js +331 -0
  140. package/tools/stuck.js +297 -0
  141. package/tools/subscribe.js +148 -0
  142. package/tools/subscriptions.js +134 -0
  143. package/tools/suggest-tags.js +184 -0
  144. package/tools/tag-suggestions.js +257 -0
  145. package/tools/telegram-bot.js +183 -0
  146. package/tools/telegram-setup.js +214 -0
  147. package/tools/tictactoe.js +155 -0
  148. package/tools/tip.js +120 -0
  149. package/tools/token.js +103 -0
  150. package/tools/twentyquestions.js +143 -0
  151. package/tools/update.js +1 -1
  152. package/tools/wallet.js +127 -0
  153. package/tools/watch.js +157 -0
  154. package/tools/webhook-test.js +388 -0
  155. package/tools/who.js +118 -25
  156. package/tools/withdraw.js +145 -0
  157. package/tools/wordassociation.js +247 -0
  158. package/tools/work-summary.js +96 -0
  159. package/tools/workshop-buddy.js +394 -0
  160. package/tools/workshop.js +327 -0
  161. package/version.json +12 -3
  162. package/tools/board.js +0 -130
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Tic-Tac-Toe tool - Play against AI with different difficulty levels
3
+ *
4
+ * Usage:
5
+ * vibe tictactoe --difficulty easy (start new game)
6
+ * vibe tictactoe --move 5 (make move)
7
+ * vibe tictactoe (show current game)
8
+ */
9
+
10
+ const config = require('../config');
11
+ const store = require('../store');
12
+ const {
13
+ createInitialTicTacToeState,
14
+ makeMove,
15
+ makeAIMove,
16
+ formatTicTacToeDisplay,
17
+ getDifficultyDescription
18
+ } = require('../games/tictactoe');
19
+ const { requireInit } = require('./_shared');
20
+
21
+ const definition = {
22
+ name: 'vibe_tictactoe',
23
+ description: 'Play tic-tac-toe against AI with different difficulty levels (easy, medium, hard)',
24
+ inputSchema: {
25
+ type: 'object',
26
+ properties: {
27
+ difficulty: {
28
+ type: 'string',
29
+ description: 'AI difficulty level',
30
+ enum: ['easy', 'medium', 'hard']
31
+ },
32
+ move: {
33
+ type: 'number',
34
+ description: 'Position to play (1-9)',
35
+ minimum: 1,
36
+ maximum: 9
37
+ },
38
+ reset: {
39
+ type: 'boolean',
40
+ description: 'Reset/start new game'
41
+ }
42
+ },
43
+ required: []
44
+ }
45
+ };
46
+
47
+ async function handler(args) {
48
+ const initCheck = requireInit();
49
+ if (initCheck) return initCheck;
50
+
51
+ const { difficulty, move, reset } = args;
52
+ const myHandle = config.getHandle();
53
+
54
+ // Get or create game state from memory
55
+ let gameState = await store.getMemory(myHandle, 'tictactoe_game');
56
+
57
+ // Start new game if requested, no game exists, or game is over
58
+ if (reset || !gameState || gameState.gameOver) {
59
+ const newDifficulty = difficulty || 'medium';
60
+ gameState = createInitialTicTacToeState(newDifficulty);
61
+ await store.setMemory(myHandle, 'tictactoe_game', gameState);
62
+
63
+ return {
64
+ display: `šŸŽÆ **New Tic-Tac-Toe Game Started!**\n\n${getDifficultyDescription(newDifficulty)}\n\n${formatTicTacToeDisplay(gameState)}\n\nšŸ’” Use \`vibe tictactoe --move N\` to play position 1-9`
65
+ };
66
+ }
67
+
68
+ // Show current game if no move specified
69
+ if (move === undefined) {
70
+ let display = `šŸŽÆ **Current Tic-Tac-Toe Game**\n\n${formatTicTacToeDisplay(gameState)}`;
71
+
72
+ if (gameState.gameOver) {
73
+ display += `\n\nšŸ’” Use \`vibe tictactoe --reset\` to start a new game`;
74
+ display += `\nšŸ’” Use \`vibe tictactoe --difficulty hard\` to start with different difficulty`;
75
+ } else {
76
+ display += `\n\nšŸ’” Use \`vibe tictactoe --move N\` to play position 1-9`;
77
+ }
78
+
79
+ return { display };
80
+ }
81
+
82
+ // Check if game is over
83
+ if (gameState.gameOver) {
84
+ return {
85
+ display: `šŸŽÆ **Game Over!**\n\n${formatTicTacToeDisplay(gameState)}\n\nšŸ’” Use \`vibe tictactoe --reset\` to start a new game`
86
+ };
87
+ }
88
+
89
+ // Check if it's player's turn
90
+ if (gameState.turn !== gameState.playerSymbol) {
91
+ return {
92
+ display: `ā³ **Not your turn!**\n\n${formatTicTacToeDisplay(gameState)}\n\nWait for AI to move.`
93
+ };
94
+ }
95
+
96
+ // Make player move
97
+ const playerMoveResult = makeMove(gameState, move, gameState.playerSymbol);
98
+
99
+ if (playerMoveResult.error) {
100
+ return {
101
+ display: `āŒ **Invalid move:** ${playerMoveResult.error}\n\n${formatTicTacToeDisplay(gameState)}\n\nšŸ’” Choose an empty position (1-9)`
102
+ };
103
+ }
104
+
105
+ gameState = playerMoveResult.gameState;
106
+
107
+ // Check if game ended after player move
108
+ if (gameState.gameOver) {
109
+ await store.setMemory(myHandle, 'tictactoe_game', gameState);
110
+
111
+ let endMessage = '';
112
+ if (gameState.winner === gameState.playerSymbol) {
113
+ endMessage = `šŸŽ‰ **Congratulations! You won!**`;
114
+ } else if (gameState.isDraw) {
115
+ endMessage = `šŸ¤ **It's a draw!** Well played!`;
116
+ }
117
+
118
+ return {
119
+ display: `${endMessage}\n\n${formatTicTacToeDisplay(gameState)}\n\nšŸ’” Use \`vibe tictactoe --reset\` to start a new game`
120
+ };
121
+ }
122
+
123
+ // Make AI move
124
+ const aiMoveResult = makeAIMove(gameState);
125
+
126
+ if (aiMoveResult.error) {
127
+ return {
128
+ display: `šŸ¤– **AI Error:** ${aiMoveResult.error}\n\n${formatTicTacToeDisplay(gameState)}`
129
+ };
130
+ }
131
+
132
+ gameState = aiMoveResult.gameState;
133
+
134
+ // Save game state
135
+ await store.setMemory(myHandle, 'tictactoe_game', gameState);
136
+
137
+ // Check if game ended after AI move
138
+ let endMessage = '';
139
+ if (gameState.gameOver) {
140
+ if (gameState.winner === gameState.aiSymbol) {
141
+ endMessage = `šŸ¤– **AI wins!** Try again!\n\n`;
142
+ } else if (gameState.isDraw) {
143
+ endMessage = `šŸ¤ **It's a draw!** Good game!\n\n`;
144
+ }
145
+ endMessage += `šŸ’” Use \`vibe tictactoe --reset\` to start a new game`;
146
+ } else {
147
+ endMessage = `šŸ’” Your turn! Use \`vibe tictactoe --move N\` to play position 1-9`;
148
+ }
149
+
150
+ return {
151
+ display: `šŸŽÆ **Move Complete**\n\n${formatTicTacToeDisplay(gameState)}\n\n${endMessage}`
152
+ };
153
+ }
154
+
155
+ module.exports = { definition, handler };
package/tools/tip.js ADDED
@@ -0,0 +1,120 @@
1
+ /**
2
+ * vibe_tip - Send a tip to another user
3
+ *
4
+ * Makes the entire payment layer accessible from Claude Code conversations
5
+ *
6
+ * Examples:
7
+ * - "tip @alice $5 for helping debug"
8
+ * - "send @bob $10 thanks for the intro"
9
+ * - "vibe tip @charlie 2.50"
10
+ */
11
+
12
+ const fetch = require('node-fetch');
13
+
14
+ module.exports = {
15
+ name: 'vibe_tip',
16
+ description: 'Send a tip to another user. Tips are instant payments via blockchain (2.5% fee). Example: vibe_tip @alice 5 "thanks for the help!"',
17
+
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ to: {
22
+ type: 'string',
23
+ description: 'Recipient handle (e.g., @alice)'
24
+ },
25
+ amount: {
26
+ type: 'number',
27
+ description: 'Amount in USD (e.g., 5.00). Min $0.01, Max $100'
28
+ },
29
+ message: {
30
+ type: 'string',
31
+ description: 'Optional message to include with the tip'
32
+ }
33
+ },
34
+ required: ['to', 'amount']
35
+ },
36
+
37
+ async execute({ to, amount, message }, context) {
38
+ try {
39
+ const from = context.handle;
40
+
41
+ if (!from) {
42
+ return {
43
+ success: false,
44
+ error: 'Not authenticated. Use vibe init first.'
45
+ };
46
+ }
47
+
48
+ // Validate amount
49
+ if (amount <= 0 || amount > 100) {
50
+ return {
51
+ success: false,
52
+ error: 'Amount must be between $0.01 and $100'
53
+ };
54
+ }
55
+
56
+ // Call payment API
57
+ const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
58
+
59
+ const response = await fetch(`${apiUrl}/api/payments/tip`, {
60
+ method: 'POST',
61
+ headers: {
62
+ 'Content-Type': 'application/json',
63
+ 'Authorization': `Bearer ${context.token}`
64
+ },
65
+ body: JSON.stringify({
66
+ from,
67
+ to,
68
+ amount,
69
+ message
70
+ })
71
+ });
72
+
73
+ const result = await response.json();
74
+
75
+ if (!response.ok) {
76
+ return {
77
+ success: false,
78
+ error: result.error || 'Tip failed',
79
+ details: result.details
80
+ };
81
+ }
82
+
83
+ // Format success message
84
+ const fee = result.fee || (amount * 0.025);
85
+ const net = amount - fee;
86
+
87
+ return {
88
+ success: true,
89
+ message: `šŸ’° Tipped ${to} $${amount}`,
90
+ details: {
91
+ recipient: to,
92
+ amount: `$${amount}`,
93
+ fee: `$${fee.toFixed(2)} (2.5%)`,
94
+ net_to_recipient: `$${net.toFixed(2)}`,
95
+ tx_hash: result.tx_hash,
96
+ status: result.status,
97
+ explorer_url: `https://sepolia.basescan.org/tx/${result.tx_hash}`
98
+ },
99
+ formatted: `
100
+ āœ“ Tip sent!
101
+ To: ${to}
102
+ Amount: $${amount}
103
+ Fee: $${fee.toFixed(2)}
104
+ Net: $${net.toFixed(2)}
105
+ ${message ? `Message: "${message}"` : ''}
106
+
107
+ Tx: ${result.tx_hash?.substring(0, 10)}...
108
+ Status: ${result.status}
109
+ `.trim()
110
+ };
111
+
112
+ } catch (error) {
113
+ return {
114
+ success: false,
115
+ error: 'Tip failed',
116
+ details: error.message
117
+ };
118
+ }
119
+ }
120
+ };
package/tools/token.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * vibe token — Set Privy auth token after browser OAuth
3
+ *
4
+ * Usage:
5
+ * vibe token <paste-token-here>
6
+ *
7
+ * After authenticating via GitHub in browser, paste the token here
8
+ * to complete authentication.
9
+ */
10
+
11
+ const config = require('../config');
12
+ const store = require('../store');
13
+
14
+ const definition = {
15
+ name: 'vibe_token',
16
+ description: 'Set your auth token after browser authentication. Use after completing GitHub OAuth in browser.',
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ token: {
21
+ type: 'string',
22
+ description: 'The auth token from browser authentication'
23
+ }
24
+ },
25
+ required: ['token']
26
+ }
27
+ };
28
+
29
+ async function handler(args) {
30
+ const { token } = args;
31
+
32
+ if (!token || token.trim().length < 10) {
33
+ return {
34
+ display: `āŒ **Invalid token**
35
+
36
+ Please paste the full token from your browser authentication.
37
+
38
+ If you haven't authenticated yet:
39
+ 1. Run \`vibe init @yourhandle\` to start
40
+ 2. Open the auth URL in your browser
41
+ 3. Complete GitHub authentication
42
+ 4. Paste the token here`
43
+ };
44
+ }
45
+
46
+ // Verify token with server
47
+ const verification = await store.verifyPrivyToken(token.trim());
48
+
49
+ if (!verification.valid) {
50
+ return {
51
+ display: `āŒ **Token verification failed**
52
+
53
+ Error: ${verification.error}
54
+
55
+ The token may be expired or invalid. Try authenticating again:
56
+ 1. Visit: ${config.getApiUrl()}/api/auth/github?handle=${config.getHandle() || 'yourhandle'}
57
+ 2. Complete GitHub authentication
58
+ 3. Paste the new token here`
59
+ };
60
+ }
61
+
62
+ // Save token
63
+ config.savePrivyToken(token.trim());
64
+
65
+ // Update session identity with verified handle
66
+ const handle = verification.handle;
67
+ const oneLiner = config.getOneLiner() || 'Building something';
68
+ config.setSessionIdentity(handle, oneLiner);
69
+
70
+ // Update shared config
71
+ const cfg = config.load();
72
+ cfg.handle = handle;
73
+ cfg.one_liner = oneLiner;
74
+ config.save(cfg);
75
+
76
+ // Remove old keypair (security improvement)
77
+ if (config.hasKeypair()) {
78
+ config.removeKeypair();
79
+ }
80
+
81
+ // Send initial heartbeat
82
+ await store.heartbeat(handle, oneLiner);
83
+
84
+ return {
85
+ display: `āœ… **Authenticated as @${handle}**
86
+
87
+ GitHub: @${verification.github || 'linked'}
88
+ Expires: ${verification.expiresAt ? new Date(verification.expiresAt * 1000).toLocaleString() : 'unknown'}
89
+
90
+ Your messages are now signed server-side (more secure).
91
+ ${config.hasKeypair() ? '' : 'šŸ”’ Old local keys have been removed.'}
92
+
93
+ You're ready to vibe! Try:
94
+ • \`vibe who\` — See who's online
95
+ • \`vibe inbox\` — Check messages
96
+ • \`vibe dm @someone "hello"\` — Send a message`,
97
+ handle,
98
+ github: verification.github,
99
+ authMethod: 'privy'
100
+ };
101
+ }
102
+
103
+ module.exports = { definition, handler };
@@ -0,0 +1,143 @@
1
+ /**
2
+ * vibe twenty-questions — Play 20 questions guessing game
3
+ *
4
+ * I think of something, you ask yes/no questions to guess what it is!
5
+ */
6
+
7
+ const config = require('../config');
8
+ const store = require('../store');
9
+ const { createGamePayload } = require('../protocol');
10
+ const { requireInit } = require('./_shared');
11
+
12
+ // 20 Questions game implementation
13
+ const twentyQuestions = require('../games/twentyquestions');
14
+
15
+ const definition = {
16
+ name: 'vibe_twenty_questions',
17
+ description: 'Play 20 questions - I think of something, you guess it with yes/no questions',
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ question: {
22
+ type: 'string',
23
+ description: 'Yes/no question to ask, or "new" to start a new game'
24
+ },
25
+ category: {
26
+ type: 'string',
27
+ description: 'Category for new games (animals, objects, food, places, activities)',
28
+ enum: ['animals', 'objects', 'food', 'places', 'activities']
29
+ }
30
+ },
31
+ required: []
32
+ }
33
+ };
34
+
35
+ /**
36
+ * Get latest 20 questions game state for user
37
+ */
38
+ async function getLatestTwentyQuestionsState(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 20 questions 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 === 'twentyquestions') {
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 { question, category } = args;
57
+ const myHandle = config.getHandle();
58
+
59
+ // Get current game state
60
+ let gameState = await getLatestTwentyQuestionsState(myHandle);
61
+
62
+ // Start new game
63
+ if (!gameState || question === 'new' || (gameState.gameOver && !question)) {
64
+ gameState = twentyQuestions.createInitialTwentyQuestionsState('guess', null, category);
65
+ const payload = createGamePayload('twentyquestions', gameState);
66
+
67
+ const categoryText = category ? ` (${category} category)` : '';
68
+ await store.sendMessage(myHandle, myHandle, `New 20 questions game started${categoryText}! I'm thinking of something...`, 'dm', payload);
69
+
70
+ const display = twentyQuestions.formatTwentyQuestionsDisplay(gameState);
71
+ return {
72
+ display: `## New 20 Questions Game\n\n${display}\n\nAsk yes/no questions: \`vibe twenty-questions --question "Is it alive?"\``
73
+ };
74
+ }
75
+
76
+ // Show current game if no question
77
+ if (!question) {
78
+ const display = twentyQuestions.formatTwentyQuestionsDisplay(gameState);
79
+ const status = gameState.gameOver ?
80
+ '\nGame over! Use `vibe twenty-questions` to start a new game.' :
81
+ '\nAsk a question: `vibe twenty-questions --question "Is it big?"`';
82
+
83
+ return {
84
+ display: `## 20 Questions Game\n\n${display}${status}`
85
+ };
86
+ }
87
+
88
+ // Process question or guess
89
+ let result;
90
+
91
+ // Check if it's a guess (starts with "is it" or "it is")
92
+ const normalizedQuestion = question.toLowerCase().trim();
93
+ if (normalizedQuestion.startsWith('is it ') || normalizedQuestion.startsWith('it is ')) {
94
+ const guess = normalizedQuestion.startsWith('is it ') ?
95
+ normalizedQuestion.substring(6) : normalizedQuestion.substring(6);
96
+ result = twentyQuestions.processGuess(gameState, guess);
97
+ } else {
98
+ result = twentyQuestions.askQuestion(gameState, question);
99
+ }
100
+
101
+ if (result.error) {
102
+ const display = twentyQuestions.formatTwentyQuestionsDisplay(gameState);
103
+ return {
104
+ display: `## 20 Questions Game\n\n${display}\n\nāŒ **${result.error}**\n\nTry again: \`vibe twenty-questions --question "Is it alive?"\``
105
+ };
106
+ }
107
+
108
+ // Update game state
109
+ const newGameState = result.gameState;
110
+ const payload = createGamePayload('twentyquestions', newGameState);
111
+
112
+ // Send message with updated state
113
+ let message;
114
+ if (newGameState.gameOver) {
115
+ if (newGameState.won) {
116
+ message = `šŸŽ‰ Correct! It was "${newGameState.item}"! You guessed it in ${newGameState.moves} questions!`;
117
+ } else {
118
+ message = `šŸ’€ Sorry! It was "${newGameState.item}". Better luck next time!`;
119
+ }
120
+ } else if (newGameState.currentQuestion) {
121
+ const { answer } = newGameState.currentQuestion;
122
+ message = `Question ${newGameState.moves}: "${question}" → **${answer.toUpperCase()}**`;
123
+ } else if (newGameState.message) {
124
+ message = newGameState.message;
125
+ } else {
126
+ message = 'Question processed!';
127
+ }
128
+
129
+ await store.sendMessage(myHandle, myHandle, message, 'dm', payload);
130
+
131
+ const display = twentyQuestions.formatTwentyQuestionsDisplay(newGameState);
132
+ const status = newGameState.gameOver ?
133
+ '\n\nšŸŽ® Use `vibe twenty-questions` to start a new game!' :
134
+ newGameState.questionsLeft === 0 ?
135
+ '\n\nNo questions left! Make your final guess: `vibe twenty-questions --question "Is it a cat?"`' :
136
+ '\n\nNext question: `vibe twenty-questions --question "Does it fly?"`';
137
+
138
+ return {
139
+ display: `## 20 Questions Game\n\n${display}${status}`
140
+ };
141
+ }
142
+
143
+ module.exports = { definition, handler };
package/tools/update.js CHANGED
@@ -49,7 +49,7 @@ async function handler(args) {
49
49
  display += `Your install doesn't support automatic updates.\n\n`;
50
50
  display += `To update, re-run the installer:\n`;
51
51
  display += `\`\`\`\n`;
52
- display += `curl -fsSL https://raw.githubusercontent.com/brightseth/vibe/main/install.sh | bash\n`;
52
+ display += `curl -fsSL https://raw.githubusercontent.com/VibeCodingInc/vibe-mcp/main/install.sh | bash\n`;
53
53
  display += `\`\`\`\n\n`;
54
54
  display += `This will migrate you to the git-based installer for future updates.\n\n`;
55
55
  display += `---\n`;
@@ -0,0 +1,127 @@
1
+ /**
2
+ * vibe_wallet - Check wallet balance and transaction history
3
+ *
4
+ * View your economic state: balance, recent transactions, earnings
5
+ *
6
+ * Examples:
7
+ * - "vibe wallet"
8
+ * - "check my balance"
9
+ * - "show recent transactions"
10
+ */
11
+
12
+ const fetch = require('node-fetch');
13
+
14
+ module.exports = {
15
+ name: 'vibe_wallet',
16
+ description: 'Check your wallet balance and recent transactions. Shows tips sent/received, escrows, and current balance.',
17
+
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ limit: {
22
+ type: 'number',
23
+ description: 'Number of recent transactions to show (default 10, max 50)',
24
+ default: 10
25
+ },
26
+ show_balance: {
27
+ type: 'boolean',
28
+ description: 'Show current balance (default true)',
29
+ default: true
30
+ }
31
+ }
32
+ },
33
+
34
+ async execute({ limit = 10, show_balance = true }, context) {
35
+ try {
36
+ const handle = context.handle;
37
+
38
+ if (!handle) {
39
+ return {
40
+ success: false,
41
+ error: 'Not authenticated. Use vibe init first.'
42
+ };
43
+ }
44
+
45
+ const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
46
+
47
+ // Fetch transaction history
48
+ const historyResponse = await fetch(
49
+ `${apiUrl}/api/payments/history?handle=${handle}&limit=${Math.min(limit, 50)}`,
50
+ {
51
+ headers: {
52
+ 'Authorization': `Bearer ${context.token}`
53
+ }
54
+ }
55
+ );
56
+
57
+ const historyResult = await historyResponse.json();
58
+
59
+ if (!historyResponse.ok) {
60
+ return {
61
+ success: false,
62
+ error: 'Failed to fetch wallet data',
63
+ details: historyResult.error
64
+ };
65
+ }
66
+
67
+ const transactions = historyResult.transactions || [];
68
+
69
+ // Calculate balance from transactions (simplified)
70
+ let balance = 0;
71
+ let totalReceived = 0;
72
+ let totalSent = 0;
73
+
74
+ transactions.forEach(tx => {
75
+ if (tx.type?.includes('received') || tx.type === 'escrow_completed') {
76
+ totalReceived += tx.amount || 0;
77
+ } else if (tx.type?.includes('sent') || tx.type === 'escrow_created') {
78
+ totalSent += tx.amount || 0;
79
+ }
80
+ });
81
+
82
+ balance = totalReceived - totalSent;
83
+
84
+ // Format transactions
85
+ const formattedTxs = transactions.slice(0, limit).map(tx => {
86
+ const isReceived = tx.type?.includes('received') || tx.type === 'escrow_completed';
87
+ const icon = isReceived ? '↓' : '↑';
88
+ const peer = isReceived ? tx.from : tx.to;
89
+ const amountStr = isReceived
90
+ ? `+$${tx.amount?.toFixed(2) || '0.00'}`
91
+ : `-$${tx.amount?.toFixed(2) || '0.00'}`;
92
+
93
+ return `${icon} ${amountStr.padEnd(10)} ${peer || 'unknown'} (${tx.status})`;
94
+ });
95
+
96
+ return {
97
+ success: true,
98
+ wallet: {
99
+ handle,
100
+ balance: `$${balance.toFixed(2)}`,
101
+ total_received: `$${totalReceived.toFixed(2)}`,
102
+ total_sent: `$${totalSent.toFixed(2)}`,
103
+ transaction_count: transactions.length
104
+ },
105
+ formatted: `
106
+ šŸ’° Wallet: ${handle}
107
+
108
+ Balance: $${balance.toFixed(2)}
109
+ Received: $${totalReceived.toFixed(2)}
110
+ Sent: $${totalSent.toFixed(2)}
111
+
112
+ Recent Transactions (${Math.min(limit, transactions.length)}):
113
+ ${formattedTxs.length > 0 ? formattedTxs.join('\n') : ' No transactions yet'}
114
+
115
+ ${historyResult.has_more ? `\n(${transactions.length - limit} more transactions...)` : ''}
116
+ `.trim()
117
+ };
118
+
119
+ } catch (error) {
120
+ return {
121
+ success: false,
122
+ error: 'Failed to fetch wallet',
123
+ details: error.message
124
+ };
125
+ }
126
+ }
127
+ };