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.
- package/README.md +1 -0
- package/analytics.js +107 -0
- package/config.js +174 -3
- package/index.js +163 -34
- package/intelligence/index.js +45 -0
- package/intelligence/infer.js +316 -0
- package/intelligence/interests.js +369 -0
- package/intelligence/patterns.js +651 -0
- package/intelligence/proactive.js +358 -0
- package/intelligence/serendipity.js +306 -0
- package/notification-emitter.js +77 -0
- package/notify.js +141 -18
- package/package.json +14 -6
- package/presence.js +5 -1
- package/protocol/index.js +88 -1
- package/protocol/telegram-commands.js +199 -0
- package/store/api.js +469 -29
- package/store/index.js +7 -7
- package/store/local.js +67 -11
- package/store/profiles.js +435 -0
- package/store/reservations.js +321 -0
- package/store/skills.js +378 -0
- package/tools/_actions.js +491 -22
- package/tools/_connection-queue.js +257 -0
- package/tools/_discovery-enhanced.js +290 -0
- package/tools/_discovery.js +439 -0
- package/tools/_proactive-discovery.js +301 -0
- package/tools/_shared/index.js +64 -0
- package/tools/_work-context.js +338 -0
- package/tools/_work-context.manual-test.js +199 -0
- package/tools/_work-context.test.js +260 -0
- package/tools/admin-inbox.js +218 -0
- package/tools/agent-treasury.js +288 -0
- package/tools/agents.js +122 -0
- package/tools/analytics.js +191 -0
- package/tools/approve.js +197 -0
- package/tools/arcade.js +173 -0
- package/tools/artifact-create.js +247 -0
- package/tools/artifact-view.js +174 -0
- package/tools/artifacts-price.js +107 -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/broadcast.js +286 -0
- package/tools/bye.js +4 -0
- package/tools/chat.js +202 -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 +675 -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 +84 -14
- package/tools/draw.js +317 -0
- package/tools/drawing.js +310 -0
- package/tools/earnings.js +126 -0
- package/tools/echo.js +16 -0
- package/tools/farcaster.js +307 -0
- package/tools/feed.js +215 -0
- package/tools/follow.js +224 -0
- package/tools/friends.js +192 -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/gig-browse.js +206 -0
- package/tools/gig-complete.js +139 -0
- package/tools/guessnumber.js +194 -0
- package/tools/hangman.js +129 -0
- package/tools/help.js +269 -0
- package/tools/idea.js +217 -0
- package/tools/inbox.js +291 -25
- package/tools/init.js +657 -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 +52 -3
- package/tools/party-game.js +314 -0
- package/tools/plan.js +225 -0
- package/tools/presence-agent.js +167 -0
- package/tools/profile.js +219 -0
- package/tools/proof-of-work.js +139 -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 +231 -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/schedule.js +367 -0
- package/tools/session.js +420 -0
- package/tools/session_price.js +128 -0
- package/tools/settings.js +200 -0
- package/tools/ship.js +188 -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 +296 -81
- package/tools/status.js +53 -6
- package/tools/storybuilder.js +331 -0
- package/tools/stuck.js +297 -0
- package/tools/subscribe.js +148 -0
- package/tools/subscriptions.js +134 -0
- package/tools/suggest-tags.js +184 -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/update.js +1 -1
- package/tools/wallet.js +127 -0
- package/tools/watch.js +157 -0
- package/tools/webhook-test.js +388 -0
- package/tools/who.js +118 -25
- package/tools/withdraw.js +145 -0
- package/tools/wordassociation.js +247 -0
- package/tools/work-summary.js +96 -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
|
@@ -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/
|
|
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`;
|
package/tools/wallet.js
ADDED
|
@@ -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
|
+
};
|