slashvibe-mcp 0.2.9 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/analytics.js +107 -0
- package/auth-store.js +148 -0
- package/auto-update.js +130 -0
- package/bridges/bridge-monitor.js +388 -0
- package/bridges/discord-bot.js +431 -0
- package/bridges/farcaster.js +299 -0
- package/bridges/telegram.js +261 -0
- package/bridges/webhook-health.js +420 -0
- package/bridges/webhook-server.js +437 -0
- package/bridges/whatsapp.js +441 -0
- package/bridges/x-webhook.js +423 -0
- package/config.js +154 -5
- package/crypto.js +3 -8
- package/games/arcade.js +406 -0
- package/games/chess.js +451 -0
- package/games/colorguess.js +343 -0
- package/games/crossword-words.js +171 -0
- package/games/crossword.js +461 -0
- package/games/drawing.js +347 -0
- package/games/gameroulette.js +300 -0
- package/games/gamerouter.js +336 -0
- package/games/gamestatus.js +337 -0
- package/games/guessnumber.js +209 -0
- package/games/hangman.js +279 -0
- package/games/memory.js +338 -0
- package/games/multiplayer-tictactoe.js +389 -0
- package/games/pixelart.js +399 -0
- package/games/quickduel.js +354 -0
- package/games/riddle.js +371 -0
- package/games/rockpaperscissors.js +291 -0
- package/games/snake.js +406 -0
- package/games/storybuilder.js +343 -0
- package/games/tictactoe.js +345 -0
- package/games/twentyquestions.js +286 -0
- package/games/twotruths.js +207 -0
- package/games/werewolf.js +508 -0
- package/games/wordassociation.js +247 -0
- package/games/wordchain.js +135 -0
- package/index.js +105 -216
- 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/memory.js +166 -0
- package/notification-emitter.js +77 -0
- package/notify.js +102 -1
- package/package.json +21 -7
- package/prompts.js +1 -1
- package/protocol/index.js +161 -1
- package/setup.js +402 -0
- package/store/api.js +528 -82
- package/store/profiles.js +160 -12
- package/tools/_actions.js +463 -16
- package/tools/_deprecated/auto-suggest-connections.js +304 -0
- package/tools/_deprecated/bootstrap-skills.js +231 -0
- package/tools/_deprecated/bridge-dashboard.js +342 -0
- package/tools/_deprecated/bridge-health.js +400 -0
- package/tools/_deprecated/bridge-live.js +384 -0
- package/tools/_deprecated/bridges.js +383 -0
- package/tools/_deprecated/colorguess.js +281 -0
- package/tools/_deprecated/discover-insights.js +379 -0
- package/tools/_deprecated/discover-momentum.js +256 -0
- package/tools/_deprecated/discovery-analytics.js +345 -0
- package/tools/_deprecated/discovery-auto-suggest.js +275 -0
- package/tools/_deprecated/discovery-bootstrap.js +267 -0
- package/tools/_deprecated/discovery-daily.js +375 -0
- package/tools/_deprecated/discovery-dashboard.js +385 -0
- package/tools/_deprecated/discovery-digest.js +314 -0
- package/tools/_deprecated/discovery-hub.js +357 -0
- package/tools/_deprecated/discovery-insights.js +384 -0
- package/tools/_deprecated/discovery-momentum.js +281 -0
- package/tools/_deprecated/discovery-monitor.js +319 -0
- package/tools/_deprecated/discovery-proactive.js +300 -0
- package/tools/_deprecated/draw.js +317 -0
- package/tools/_deprecated/farcaster.js +307 -0
- package/tools/_deprecated/forget.js +119 -0
- package/tools/_deprecated/games-catalog.js +376 -0
- package/tools/_deprecated/games.js +313 -0
- package/tools/_deprecated/guessnumber.js +194 -0
- package/tools/_deprecated/hangman.js +129 -0
- package/tools/_deprecated/multiplayer-tictactoe.js +303 -0
- package/tools/_deprecated/recall.js +147 -0
- package/tools/_deprecated/remember.js +86 -0
- package/tools/_deprecated/riddle.js +240 -0
- package/tools/_deprecated/run-bootstrap.js +69 -0
- package/tools/_deprecated/skills-analytics.js +349 -0
- package/tools/_deprecated/skills-bootstrap.js +301 -0
- package/tools/_deprecated/skills-dashboard.js +268 -0
- package/tools/_deprecated/skills.js +380 -0
- package/tools/_deprecated/smart-intro.js +353 -0
- package/tools/_deprecated/storybuilder.js +331 -0
- package/tools/_deprecated/telegram-bot.js +183 -0
- package/tools/_deprecated/telegram-setup.js +214 -0
- package/tools/_deprecated/twentyquestions.js +143 -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/_shared.js +234 -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/activity.js +220 -0
- package/tools/admin-inbox.js +218 -0
- package/tools/agent-treasury.js +288 -0
- package/tools/analytics.js +191 -0
- package/tools/approve.js +197 -0
- package/tools/arcade.js +173 -0
- package/tools/artifact-create.js +17 -11
- package/tools/artifact-view.js +31 -1
- package/tools/artifacts-price.js +47 -43
- package/tools/ask-expert.js +160 -0
- package/tools/available.js +120 -0
- package/tools/become-expert.js +150 -0
- package/tools/broadcast.js +286 -0
- package/tools/chat.js +202 -0
- package/tools/collaborative-drawing.js +286 -0
- package/tools/connection-status.js +178 -0
- package/tools/crossword.js +17 -1
- package/tools/discover.js +350 -34
- package/tools/dm.js +115 -73
- package/tools/drawing.js +1 -1
- package/tools/earnings.js +126 -0
- package/tools/echo.js +16 -0
- package/tools/feed.js +51 -7
- package/tools/follow.js +224 -0
- package/tools/friends.js +207 -0
- package/tools/game.js +2 -2
- package/tools/genesis.js +233 -0
- package/tools/gig-browse.js +206 -0
- package/tools/gig-complete.js +144 -0
- package/tools/handoff.js +7 -1
- package/tools/help.js +4 -4
- package/tools/idea.js +9 -2
- package/tools/inbox.js +334 -28
- package/tools/init.js +727 -36
- package/tools/invite.js +15 -4
- 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 +1 -1
- package/tools/notifications.js +415 -0
- package/tools/observe.js +200 -0
- package/tools/onboarding.js +147 -0
- package/tools/open.js +143 -12
- package/tools/party-game.js +2 -2
- package/tools/plan.js +225 -0
- package/tools/presence-agent.js +7 -0
- package/tools/proof-of-work.js +100 -104
- package/tools/pulse.js +218 -0
- package/tools/reply.js +166 -0
- package/tools/report.js +2 -2
- package/tools/reputation.js +175 -0
- package/tools/request.js +17 -3
- package/tools/schedule.js +367 -0
- package/tools/search-messages.js +123 -0
- package/tools/session.js +420 -0
- package/tools/session_price.js +128 -0
- package/tools/settings.js +126 -3
- package/tools/ship.js +31 -8
- package/tools/shipback.js +326 -0
- package/tools/smart-check.js +201 -0
- package/tools/social-processor.js +445 -0
- package/tools/solo-game.js +1 -1
- package/tools/start.js +335 -93
- package/tools/status.js +53 -6
- package/tools/stuck.js +297 -0
- package/tools/subscribe.js +148 -0
- package/tools/subscriptions.js +134 -0
- package/tools/suggest-tags.js +6 -8
- package/tools/tag-suggestions.js +257 -0
- package/tools/tip.js +193 -0
- package/tools/token.js +103 -0
- package/tools/update.js +1 -1
- package/tools/wallet.js +239 -186
- package/tools/watch.js +157 -0
- package/tools/webhook-test.js +388 -0
- package/tools/who.js +54 -3
- package/tools/withdraw.js +145 -0
- package/tools/work-summary.js +96 -0
- package/tools/workshop.js +327 -0
- package/tools/x-mentions.js +1 -1
- package/version.json +14 -3
- package/tools/artifacts-buy.js +0 -111
- package/tools/connect.js +0 -284
- package/tools/gigs-apply.js +0 -99
- package/tools/gigs-browse.js +0 -114
- package/tools/gigs-complete.js +0 -128
- package/tools/gigs-post.js +0 -140
- package/tools/live-off.js +0 -109
- package/tools/live-watch.js +0 -176
- package/tools/live.js +0 -128
- package/tools/sessions-browse.js +0 -105
- package/tools/whats-happening.js +0 -125
- /package/tools/{away.js → _deprecated/away.js} +0 -0
- /package/tools/{back.js → _deprecated/back.js} +0 -0
- /package/tools/{mute.js → _deprecated/mute.js} +0 -0
- /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
- /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
- /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
package/tools/chat.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe chat — Live chat during broadcasts
|
|
3
|
+
*
|
|
4
|
+
* For broadcasters: See chat messages and reactions from viewers
|
|
5
|
+
* For viewers: Send messages to active broadcasts
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* - chat → Show recent chat (if broadcasting or watching)
|
|
9
|
+
* - chat "message" → Send message to current broadcast
|
|
10
|
+
* - chat --room room_xxx → Show chat for specific room
|
|
11
|
+
* - chat "message" @handle → Send to someone's broadcast
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const config = require('../config');
|
|
15
|
+
|
|
16
|
+
const definition = {
|
|
17
|
+
name: 'vibe_chat',
|
|
18
|
+
description: 'See and send chat messages during live broadcasts. Broadcasters see viewer engagement in real-time.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
message: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Message to send (omit to just view chat)'
|
|
25
|
+
},
|
|
26
|
+
room: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Room ID to view/send to'
|
|
29
|
+
},
|
|
30
|
+
target: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Handle of broadcaster to message (@user)'
|
|
33
|
+
},
|
|
34
|
+
limit: {
|
|
35
|
+
type: 'number',
|
|
36
|
+
description: 'Number of messages to show (default: 15)'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
async function handler(args) {
|
|
43
|
+
const myHandle = config.getHandle();
|
|
44
|
+
const apiUrl = config.getApiUrl();
|
|
45
|
+
const limit = args.limit || 15;
|
|
46
|
+
|
|
47
|
+
// Determine which room we're interacting with
|
|
48
|
+
let roomId = args.room;
|
|
49
|
+
const targetHandle = args.target?.replace('@', '').toLowerCase();
|
|
50
|
+
|
|
51
|
+
// If no room specified, check if we're broadcasting
|
|
52
|
+
if (!roomId) {
|
|
53
|
+
const activeBroadcast = config.get('broadcast');
|
|
54
|
+
if (activeBroadcast) {
|
|
55
|
+
roomId = activeBroadcast.roomId;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If target handle specified, find their room
|
|
60
|
+
if (targetHandle && !roomId) {
|
|
61
|
+
try {
|
|
62
|
+
const liveResponse = await fetch(`${apiUrl}/api/live`, {
|
|
63
|
+
headers: { 'Accept': 'application/json' }
|
|
64
|
+
});
|
|
65
|
+
const liveData = await liveResponse.json();
|
|
66
|
+
|
|
67
|
+
if (liveData.success && liveData.broadcasts) {
|
|
68
|
+
const broadcast = liveData.broadcasts.find(
|
|
69
|
+
b => b.handle.toLowerCase() === targetHandle
|
|
70
|
+
);
|
|
71
|
+
if (broadcast) {
|
|
72
|
+
roomId = broadcast.roomId;
|
|
73
|
+
} else {
|
|
74
|
+
return {
|
|
75
|
+
display: `📺 @${targetHandle} is not live right now.\n\n_Check who's streaming with \`vibe watch\`_`
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return { display: `## Error\n\n${error.message}` };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// If still no room, show error
|
|
85
|
+
if (!roomId) {
|
|
86
|
+
let display = `💬 **No Active Broadcast**\n\n`;
|
|
87
|
+
display += `To view/send chat, either:\n`;
|
|
88
|
+
display += `1. Start broadcasting: \`vibe broadcast start\`\n`;
|
|
89
|
+
display += `2. Specify a room: \`vibe chat --room room_xxx\`\n`;
|
|
90
|
+
display += `3. Specify a user: \`vibe chat @handle\``;
|
|
91
|
+
return { display };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// SEND message
|
|
95
|
+
if (args.message) {
|
|
96
|
+
if (!myHandle) {
|
|
97
|
+
return { display: '⚠️ Not initialized. Run `vibe init` first to send messages.' };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const response = await fetch(`${apiUrl}/api/watch/chat`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: { 'Content-Type': 'application/json' },
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
roomId,
|
|
106
|
+
message: args.message,
|
|
107
|
+
handle: myHandle
|
|
108
|
+
})
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const data = await response.json();
|
|
112
|
+
|
|
113
|
+
if (!data.success) {
|
|
114
|
+
if (data.error?.includes('not found')) {
|
|
115
|
+
return { display: `📺 Broadcast has ended.` };
|
|
116
|
+
}
|
|
117
|
+
return { display: `⚠️ Failed to send: ${data.error}` };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
display: `💬 Sent to chat: "${args.message}"`
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
} catch (error) {
|
|
125
|
+
return { display: `## Chat Error\n\n${error.message}` };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// VIEW chat and reactions
|
|
130
|
+
try {
|
|
131
|
+
// Fetch chat and reactions in parallel
|
|
132
|
+
const [chatResponse, reactResponse, broadcastResponse] = await Promise.all([
|
|
133
|
+
fetch(`${apiUrl}/api/watch/chat?room=${roomId}&limit=${limit}`),
|
|
134
|
+
fetch(`${apiUrl}/api/watch/react?room=${roomId}&limit=20`),
|
|
135
|
+
fetch(`${apiUrl}/api/watch?room=${roomId}`)
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
const chatData = await chatResponse.json();
|
|
139
|
+
const reactData = await reactResponse.json();
|
|
140
|
+
const broadcastData = await broadcastResponse.json();
|
|
141
|
+
|
|
142
|
+
if (!broadcastData.success) {
|
|
143
|
+
return { display: `📺 Broadcast not found or ended.` };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const broadcast = broadcastData.broadcast;
|
|
147
|
+
const messages = chatData.messages || [];
|
|
148
|
+
const reactions = reactData.reactions || [];
|
|
149
|
+
|
|
150
|
+
// Build display
|
|
151
|
+
let display = `💬 **Chat** — @${broadcast.handle}'s broadcast\n`;
|
|
152
|
+
display += `👥 ${broadcast.viewerCount || 0} watching`;
|
|
153
|
+
if (chatData.messagesPerMinute > 0) {
|
|
154
|
+
display += ` · ${chatData.messagesPerMinute} msg/min`;
|
|
155
|
+
}
|
|
156
|
+
display += `\n\n`;
|
|
157
|
+
|
|
158
|
+
// Recent reactions summary
|
|
159
|
+
if (reactions.length > 0) {
|
|
160
|
+
const reactionCounts = {};
|
|
161
|
+
for (const r of reactions.slice(0, 20)) {
|
|
162
|
+
reactionCounts[r.emoji] = (reactionCounts[r.emoji] || 0) + 1;
|
|
163
|
+
}
|
|
164
|
+
const reactionStr = Object.entries(reactionCounts)
|
|
165
|
+
.map(([emoji, count]) => `${emoji}×${count}`)
|
|
166
|
+
.join(' ');
|
|
167
|
+
display += `**Reactions:** ${reactionStr}\n\n`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Chat messages
|
|
171
|
+
if (messages.length === 0) {
|
|
172
|
+
display += `_No messages yet. Be the first to chat!_\n`;
|
|
173
|
+
display += `\`vibe chat "your message"\``;
|
|
174
|
+
} else {
|
|
175
|
+
display += `**Recent Messages:**\n`;
|
|
176
|
+
for (const msg of messages.slice(-limit)) {
|
|
177
|
+
const time = formatTime(msg.timestamp);
|
|
178
|
+
const badge = msg.isBroadcaster ? ' 🎙️' : '';
|
|
179
|
+
display += `[${time}] **@${msg.handle}**${badge}: ${msg.message}\n`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
display += `\n---\n`;
|
|
184
|
+
display += `_Reply: \`vibe chat "your message"\`_`;
|
|
185
|
+
|
|
186
|
+
return { display };
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
return { display: `## Chat Error\n\n${error.message}` };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function formatTime(timestamp) {
|
|
194
|
+
const date = new Date(timestamp);
|
|
195
|
+
return date.toLocaleTimeString('en-US', {
|
|
196
|
+
hour: 'numeric',
|
|
197
|
+
minute: '2-digit',
|
|
198
|
+
hour12: true
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = { definition, handler };
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collaborative Drawing — Create or join a shared drawing session
|
|
3
|
+
*
|
|
4
|
+
* Multiple users can draw together on a shared canvas in real-time.
|
|
5
|
+
* Perfect for sketching, doodling, or playing Pictionary together!
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const config = require('../config');
|
|
9
|
+
const store = require('../store');
|
|
10
|
+
const { requireInit, normalizeHandle } = require('./_shared');
|
|
11
|
+
const drawing = require('../games/drawing');
|
|
12
|
+
|
|
13
|
+
const definition = {
|
|
14
|
+
name: 'vibe_collaborative_drawing',
|
|
15
|
+
description: 'Create or join a collaborative drawing session with multiple users. Draw together on a shared canvas!',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
room: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Drawing room name (creates if doesn\'t exist, e.g., "art-party", "sketch-together")'
|
|
22
|
+
},
|
|
23
|
+
action: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Action to take',
|
|
26
|
+
enum: ['view', 'draw', 'line', 'clear', 'theme']
|
|
27
|
+
},
|
|
28
|
+
x: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
description: 'X coordinate (0-19 for draw/line actions)'
|
|
31
|
+
},
|
|
32
|
+
y: {
|
|
33
|
+
type: 'number',
|
|
34
|
+
description: 'Y coordinate (0-11 for draw/line actions)'
|
|
35
|
+
},
|
|
36
|
+
x1: {
|
|
37
|
+
type: 'number',
|
|
38
|
+
description: 'End X coordinate (for line action)'
|
|
39
|
+
},
|
|
40
|
+
y1: {
|
|
41
|
+
type: 'number',
|
|
42
|
+
description: 'End Y coordinate (for line action)'
|
|
43
|
+
},
|
|
44
|
+
char: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Character/emoji to draw (e.g., "star", "heart", "tree"). See available chars in canvas display.'
|
|
47
|
+
},
|
|
48
|
+
theme: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: 'Drawing theme/prompt to set for the room'
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
required: ['room']
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Storage key for drawing sessions
|
|
58
|
+
function getDrawingRoomKey(roomName) {
|
|
59
|
+
return `drawing_room:${roomName.toLowerCase()}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Get or create drawing room
|
|
63
|
+
async function getOrCreateRoom(roomName) {
|
|
64
|
+
const key = getDrawingRoomKey(roomName);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const existing = await store.kv.get(key);
|
|
68
|
+
if (existing) {
|
|
69
|
+
return JSON.parse(existing);
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.log(`[drawing] Creating new room: ${roomName}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Create new room
|
|
76
|
+
const newRoom = drawing.createInitialDrawingState();
|
|
77
|
+
await store.kv.set(key, JSON.stringify(newRoom));
|
|
78
|
+
return newRoom;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Save room state
|
|
82
|
+
async function saveRoom(roomName, gameState) {
|
|
83
|
+
const key = getDrawingRoomKey(roomName);
|
|
84
|
+
await store.kv.set(key, JSON.stringify(gameState));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Post drawing activity to board
|
|
88
|
+
async function postDrawingActivity(roomName, playerHandle, action, details = '') {
|
|
89
|
+
const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
let content = '';
|
|
93
|
+
switch (action) {
|
|
94
|
+
case 'created':
|
|
95
|
+
content = `🎨 @${playerHandle} created collaborative drawing room "${roomName}" — join in!`;
|
|
96
|
+
break;
|
|
97
|
+
case 'joined':
|
|
98
|
+
content = `🎨 @${playerHandle} joined the "${roomName}" drawing session`;
|
|
99
|
+
break;
|
|
100
|
+
case 'drew':
|
|
101
|
+
content = `🎨 @${playerHandle} is drawing in "${roomName}" ${details}`;
|
|
102
|
+
break;
|
|
103
|
+
case 'theme':
|
|
104
|
+
content = `🎯 @${playerHandle} set drawing theme in "${roomName}": ${details}`;
|
|
105
|
+
break;
|
|
106
|
+
default:
|
|
107
|
+
return; // Don't post for other actions
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
await fetch(`${API_URL}/api/board`, {
|
|
111
|
+
method: 'POST',
|
|
112
|
+
headers: { 'Content-Type': 'application/json' },
|
|
113
|
+
body: JSON.stringify({
|
|
114
|
+
author: 'echo',
|
|
115
|
+
content,
|
|
116
|
+
category: 'general'
|
|
117
|
+
})
|
|
118
|
+
});
|
|
119
|
+
} catch (e) {
|
|
120
|
+
console.error('[drawing] Failed to post to board:', e.message);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function handler(args) {
|
|
125
|
+
const initCheck = requireInit();
|
|
126
|
+
if (initCheck) return initCheck;
|
|
127
|
+
|
|
128
|
+
const { room, action = 'view', x, y, x1, y1, char, theme } = args;
|
|
129
|
+
const myHandle = config.getHandle();
|
|
130
|
+
|
|
131
|
+
if (!room) {
|
|
132
|
+
return { display: 'Room name is required. Use something like "art-party" or "sketch-together"' };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Normalize room name
|
|
136
|
+
const roomName = room.toLowerCase().replace(/[^a-z0-9-]/g, '-');
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
// Get or create room
|
|
140
|
+
let gameState = await getOrCreateRoom(roomName);
|
|
141
|
+
const wasNewRoom = gameState.players.length === 0;
|
|
142
|
+
|
|
143
|
+
// Add player to room if not already there
|
|
144
|
+
if (!gameState.players.includes(myHandle)) {
|
|
145
|
+
const addResult = drawing.addPlayer(gameState, myHandle);
|
|
146
|
+
if (addResult.error) {
|
|
147
|
+
return { display: addResult.error };
|
|
148
|
+
}
|
|
149
|
+
gameState = addResult.gameState;
|
|
150
|
+
await saveRoom(roomName, gameState);
|
|
151
|
+
|
|
152
|
+
// Post activity
|
|
153
|
+
if (wasNewRoom) {
|
|
154
|
+
await postDrawingActivity(roomName, myHandle, 'created');
|
|
155
|
+
} else {
|
|
156
|
+
await postDrawingActivity(roomName, myHandle, 'joined');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Handle different actions
|
|
161
|
+
switch (action) {
|
|
162
|
+
case 'view':
|
|
163
|
+
// Just show current state
|
|
164
|
+
break;
|
|
165
|
+
|
|
166
|
+
case 'draw':
|
|
167
|
+
if (x === undefined || y === undefined || !char) {
|
|
168
|
+
return { display: 'Draw action requires x, y coordinates and char. Example: `--action draw --x 10 --y 5 --char heart`' };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Validate character name
|
|
172
|
+
const drawChar = drawing.DRAWING_CHARS[char];
|
|
173
|
+
if (!drawChar) {
|
|
174
|
+
const availableChars = Object.keys(drawing.DRAWING_CHARS).join(', ');
|
|
175
|
+
return { display: `Invalid character "${char}". Available: ${availableChars}` };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const drawResult = drawing.makeMove(gameState, x, y, drawChar, myHandle);
|
|
179
|
+
if (drawResult.error) {
|
|
180
|
+
return { display: drawResult.error };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
gameState = drawResult.gameState;
|
|
184
|
+
await saveRoom(roomName, gameState);
|
|
185
|
+
await postDrawingActivity(roomName, myHandle, 'drew', `(${drawChar} at ${x},${y})`);
|
|
186
|
+
break;
|
|
187
|
+
|
|
188
|
+
case 'line':
|
|
189
|
+
if (x === undefined || y === undefined || x1 === undefined || y1 === undefined || !char) {
|
|
190
|
+
return { display: 'Line action requires x, y, x1, y1 coordinates and char. Example: `--action line --x 5 --y 5 --x1 15 --y1 8 --char star`' };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const lineChar = drawing.DRAWING_CHARS[char];
|
|
194
|
+
if (!lineChar) {
|
|
195
|
+
const availableChars = Object.keys(drawing.DRAWING_CHARS).join(', ');
|
|
196
|
+
return { display: `Invalid character "${char}". Available: ${availableChars}` };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const lineResult = drawing.drawLine(gameState, x, y, x1, y1, lineChar, myHandle);
|
|
200
|
+
if (lineResult.error) {
|
|
201
|
+
return { display: lineResult.error };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
gameState = lineResult.gameState;
|
|
205
|
+
await saveRoom(roomName, gameState);
|
|
206
|
+
await postDrawingActivity(roomName, myHandle, 'drew', `line from (${x},${y}) to (${x1},${y1})`);
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
case 'clear':
|
|
210
|
+
if (x === undefined || y === undefined) {
|
|
211
|
+
// Clear entire canvas
|
|
212
|
+
const clearResult = drawing.clearRegion(gameState, 0, 0, drawing.CANVAS_WIDTH - 1, drawing.CANVAS_HEIGHT - 1, myHandle);
|
|
213
|
+
if (clearResult.error) {
|
|
214
|
+
return { display: clearResult.error };
|
|
215
|
+
}
|
|
216
|
+
gameState = clearResult.gameState;
|
|
217
|
+
await postDrawingActivity(roomName, myHandle, 'drew', '(cleared canvas)');
|
|
218
|
+
} else {
|
|
219
|
+
// Clear specific region
|
|
220
|
+
const clearX1 = x1 !== undefined ? x1 : x;
|
|
221
|
+
const clearY1 = y1 !== undefined ? y1 : y;
|
|
222
|
+
const clearResult = drawing.clearRegion(gameState, x, y, clearX1, clearY1, myHandle);
|
|
223
|
+
if (clearResult.error) {
|
|
224
|
+
return { display: clearResult.error };
|
|
225
|
+
}
|
|
226
|
+
gameState = clearResult.gameState;
|
|
227
|
+
await postDrawingActivity(roomName, myHandle, 'drew', `(cleared region ${x},${y} to ${clearX1},${clearY1})`);
|
|
228
|
+
}
|
|
229
|
+
await saveRoom(roomName, gameState);
|
|
230
|
+
break;
|
|
231
|
+
|
|
232
|
+
case 'theme':
|
|
233
|
+
if (!theme) {
|
|
234
|
+
return { display: 'Theme action requires a theme. Example: `--action theme --theme "draw a house"`' };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const themeResult = drawing.setTheme(gameState, theme, myHandle);
|
|
238
|
+
if (themeResult.error) {
|
|
239
|
+
return { display: themeResult.error };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
gameState = themeResult.gameState;
|
|
243
|
+
await saveRoom(roomName, gameState);
|
|
244
|
+
await postDrawingActivity(roomName, myHandle, 'theme', theme);
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
default:
|
|
248
|
+
return { display: `Unknown action "${action}". Use: view, draw, line, clear, or theme` };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Format and return display
|
|
252
|
+
const display = drawing.formatDrawingDisplay(gameState);
|
|
253
|
+
const stats = drawing.getCanvasStats(gameState);
|
|
254
|
+
|
|
255
|
+
let result = `# 🎨 Collaborative Drawing Room: "${roomName}"\n\n${display}`;
|
|
256
|
+
|
|
257
|
+
// Add usage instructions
|
|
258
|
+
result += '\n**How to draw:**\n';
|
|
259
|
+
result += `• \`vibe collaborative-drawing --room "${roomName}" --action draw --x 10 --y 5 --char heart\`\n`;
|
|
260
|
+
result += `• \`vibe collaborative-drawing --room "${roomName}" --action line --x 5 --y 3 --x1 15 --y1 8 --char star\`\n`;
|
|
261
|
+
result += `• \`vibe collaborative-drawing --room "${roomName}" --action clear\` (clear all)\n`;
|
|
262
|
+
result += `• \`vibe collaborative-drawing --room "${roomName}" --action theme --theme "draw a sunset"\`\n\n`;
|
|
263
|
+
|
|
264
|
+
// Add tips if there's a theme
|
|
265
|
+
if (gameState.theme) {
|
|
266
|
+
const tips = drawing.getDrawingTips(gameState.theme);
|
|
267
|
+
result += `**Drawing tips for "${gameState.theme}":**\n`;
|
|
268
|
+
result += tips.map(tip => `• ${tip}`).join('\n') + '\n\n';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Add stats
|
|
272
|
+
result += `**Canvas stats:** ${stats.fillPercentage}% filled, ${stats.totalMoves} moves, ${stats.uniqueCharsUsed} different characters used\n`;
|
|
273
|
+
|
|
274
|
+
if (stats.mostUsedChar) {
|
|
275
|
+
result += `Most used: ${stats.mostUsedChar[0]} (${stats.mostUsedChar[1]} times)\n`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return { display: result };
|
|
279
|
+
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('[drawing] Error:', error);
|
|
282
|
+
return { display: `Error: ${error.message}` };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
module.exports = { definition, handler };
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_connection_status - Check connection health
|
|
3
|
+
*
|
|
4
|
+
* Shows if you're connected to the /vibe network and API health.
|
|
5
|
+
* Helps debug connection issues.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const config = require('../config');
|
|
9
|
+
|
|
10
|
+
const connectionStatusSchema = {
|
|
11
|
+
name: 'vibe_connection_status',
|
|
12
|
+
description: 'Check your connection status to /vibe. Shows API health, latency, and session info.',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function connectionStatusHandler() {
|
|
20
|
+
const handle = config.getHandle();
|
|
21
|
+
const apiUrl = config.getApiUrl();
|
|
22
|
+
const token = config.getToken();
|
|
23
|
+
|
|
24
|
+
const results = {
|
|
25
|
+
initialized: !!handle,
|
|
26
|
+
handle: handle || null,
|
|
27
|
+
apiUrl,
|
|
28
|
+
hasToken: !!token,
|
|
29
|
+
checks: {},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Check API health
|
|
33
|
+
const healthStart = Date.now();
|
|
34
|
+
try {
|
|
35
|
+
const response = await fetch(`${apiUrl}/api/health`, {
|
|
36
|
+
signal: AbortSignal.timeout(5000),
|
|
37
|
+
});
|
|
38
|
+
const health = await response.json();
|
|
39
|
+
results.checks.api = {
|
|
40
|
+
status: health.status === 'healthy' ? '✅ healthy' : '⚠️ degraded',
|
|
41
|
+
latency: Date.now() - healthStart,
|
|
42
|
+
version: health.version,
|
|
43
|
+
};
|
|
44
|
+
} catch (e) {
|
|
45
|
+
results.checks.api = {
|
|
46
|
+
status: '❌ unreachable',
|
|
47
|
+
latency: Date.now() - healthStart,
|
|
48
|
+
error: e.message,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check auth (if we have a token)
|
|
53
|
+
if (token && handle) {
|
|
54
|
+
const authStart = Date.now();
|
|
55
|
+
try {
|
|
56
|
+
const response = await fetch(`${apiUrl}/api/v2/presence?user=${handle}`, {
|
|
57
|
+
headers: {
|
|
58
|
+
'Authorization': `Bearer ${token}`,
|
|
59
|
+
},
|
|
60
|
+
signal: AbortSignal.timeout(5000),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
if (response.status === 401) {
|
|
64
|
+
results.checks.auth = {
|
|
65
|
+
status: '❌ token expired',
|
|
66
|
+
latency: Date.now() - authStart,
|
|
67
|
+
action: 'Run `vibe init` to re-authenticate',
|
|
68
|
+
};
|
|
69
|
+
} else if (response.ok) {
|
|
70
|
+
results.checks.auth = {
|
|
71
|
+
status: '✅ authenticated',
|
|
72
|
+
latency: Date.now() - authStart,
|
|
73
|
+
};
|
|
74
|
+
} else {
|
|
75
|
+
results.checks.auth = {
|
|
76
|
+
status: '⚠️ error',
|
|
77
|
+
latency: Date.now() - authStart,
|
|
78
|
+
httpStatus: response.status,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
results.checks.auth = {
|
|
83
|
+
status: '❌ failed',
|
|
84
|
+
latency: Date.now() - authStart,
|
|
85
|
+
error: e.message,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check presence write (can we heartbeat?)
|
|
91
|
+
if (token && handle) {
|
|
92
|
+
const presenceStart = Date.now();
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch(`${apiUrl}/api/presence`, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: {
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
'Authorization': `Bearer ${token}`,
|
|
99
|
+
},
|
|
100
|
+
body: JSON.stringify({
|
|
101
|
+
username: handle,
|
|
102
|
+
workingOn: 'Checking connection status',
|
|
103
|
+
}),
|
|
104
|
+
signal: AbortSignal.timeout(5000),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (response.ok) {
|
|
108
|
+
results.checks.presence = {
|
|
109
|
+
status: '✅ online',
|
|
110
|
+
latency: Date.now() - presenceStart,
|
|
111
|
+
};
|
|
112
|
+
} else {
|
|
113
|
+
results.checks.presence = {
|
|
114
|
+
status: '⚠️ error',
|
|
115
|
+
latency: Date.now() - presenceStart,
|
|
116
|
+
httpStatus: response.status,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
} catch (e) {
|
|
120
|
+
results.checks.presence = {
|
|
121
|
+
status: '❌ offline',
|
|
122
|
+
latency: Date.now() - presenceStart,
|
|
123
|
+
error: e.message,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Format output
|
|
129
|
+
let output = '## /vibe Connection Status\n\n';
|
|
130
|
+
|
|
131
|
+
if (!results.initialized) {
|
|
132
|
+
output += '❌ **Not initialized**\n';
|
|
133
|
+
output += 'Run `vibe init` to get started.\n';
|
|
134
|
+
return { content: [{ type: 'text', text: output }] };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
output += `**Handle:** @${results.handle}\n`;
|
|
138
|
+
output += `**API:** ${results.apiUrl}\n\n`;
|
|
139
|
+
|
|
140
|
+
output += '### Health Checks\n\n';
|
|
141
|
+
|
|
142
|
+
for (const [name, check] of Object.entries(results.checks)) {
|
|
143
|
+
output += `**${name}:** ${check.status}`;
|
|
144
|
+
if (check.latency) {
|
|
145
|
+
output += ` (${check.latency}ms)`;
|
|
146
|
+
}
|
|
147
|
+
output += '\n';
|
|
148
|
+
|
|
149
|
+
if (check.error) {
|
|
150
|
+
output += ` _Error: ${check.error}_\n`;
|
|
151
|
+
}
|
|
152
|
+
if (check.action) {
|
|
153
|
+
output += ` → ${check.action}\n`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Overall status
|
|
158
|
+
const allHealthy = Object.values(results.checks).every(c =>
|
|
159
|
+
c.status.includes('✅')
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
output += '\n';
|
|
163
|
+
if (allHealthy) {
|
|
164
|
+
output += '🟢 **All systems operational**\n';
|
|
165
|
+
} else {
|
|
166
|
+
output += '🟡 **Some issues detected** — check details above\n';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
content: [{ type: 'text', text: output }],
|
|
171
|
+
connectionStatus: results,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
schema: connectionStatusSchema,
|
|
177
|
+
handler: connectionStatusHandler,
|
|
178
|
+
};
|
package/tools/crossword.js
CHANGED
|
@@ -19,7 +19,7 @@ const DAILY_ROOM_PREFIX = 'daily-';
|
|
|
19
19
|
|
|
20
20
|
const definition = {
|
|
21
21
|
name: 'vibe_crossword',
|
|
22
|
-
description: '
|
|
22
|
+
description: 'Play multiplayer crossword puzzles. Daily 5x5 minis, collaborative or competitive.',
|
|
23
23
|
inputSchema: {
|
|
24
24
|
type: 'object',
|
|
25
25
|
properties: {
|
|
@@ -200,6 +200,22 @@ function listRooms() {
|
|
|
200
200
|
|
|
201
201
|
// Handler
|
|
202
202
|
async function handler(args) {
|
|
203
|
+
// Check if crossword module is available
|
|
204
|
+
if (!crossword.isAvailable()) {
|
|
205
|
+
return {
|
|
206
|
+
display: `**Crossword not available**
|
|
207
|
+
|
|
208
|
+
The crossword-layout-generator module isn't installed.
|
|
209
|
+
|
|
210
|
+
**To fix:**
|
|
211
|
+
\`\`\`bash
|
|
212
|
+
cd ~/.vibe/vibe-repo/mcp-server && npm install
|
|
213
|
+
\`\`\`
|
|
214
|
+
|
|
215
|
+
Then restart Claude Code.`
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
203
219
|
// Require init
|
|
204
220
|
const initCheck = requireInit();
|
|
205
221
|
if (initCheck) {
|