slashvibe-mcp 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +280 -47
- package/auto-update.js +10 -15
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/debug.js +12 -0
- package/discord.js +19 -19
- package/eslint.config.js +54 -0
- package/index.js +217 -207
- package/intelligence/index.js +2 -9
- package/intelligence/infer.js +10 -16
- package/intelligence/patterns.js +23 -18
- package/intelligence/proactive.js +16 -15
- package/intelligence/serendipity.js +57 -20
- package/memory.js +13 -8
- package/migrate-v2.js +72 -0
- package/notification-emitter.js +2 -2
- package/notify.js +39 -14
- package/package.json +28 -29
- package/post-install.js +141 -0
- package/presence.js +2 -2
- package/prompts.js +5 -9
- package/protocol/index.js +123 -87
- package/protocol/telegram-commands.js +36 -37
- package/store/api.js +358 -529
- package/store/local.js +9 -10
- package/store/profiles.js +48 -192
- package/store/reservations.js +2 -9
- package/store/skills.js +69 -71
- package/store/sqlite.js +355 -0
- package/test-skills-bootstrap.js +20 -0
- package/test-v2-integration.js +385 -0
- package/tools/_actions.js +48 -387
- package/tools/_connection-queue.js +45 -56
- package/tools/_discovery-enhanced.js +52 -57
- package/tools/_discovery.js +87 -185
- package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
- package/tools/{shipback.js → _experimental/shipback.js} +4 -3
- package/tools/_proactive-discovery.js +60 -73
- package/tools/_shared/index.js +41 -64
- package/tools/admin-inbox.js +10 -15
- package/tools/agents.js +1 -1
- package/tools/artifact-create.js +13 -23
- package/tools/artifact-view.js +4 -4
- package/tools/{_deprecated/back.js → back.js} +1 -1
- package/tools/bye.js +3 -5
- package/tools/consent.js +2 -2
- package/tools/context.js +9 -10
- package/tools/crossword.js +3 -2
- package/tools/discover.js +94 -356
- package/tools/dm.js +27 -86
- package/tools/doctor.js +12 -41
- package/tools/drawing.js +34 -20
- package/tools/echo.js +11 -11
- package/tools/feed.js +30 -58
- package/tools/follow.js +64 -187
- package/tools/{_deprecated/forget.js → forget.js} +4 -7
- package/tools/game.js +144 -48
- package/tools/handoff.js +6 -8
- package/tools/help.js +3 -3
- package/tools/idea.js +15 -27
- package/tools/inbox.js +121 -293
- package/tools/init.js +54 -151
- package/tools/invite.js +8 -21
- package/tools/migrate.js +27 -24
- package/tools/multiplayer-game.js +50 -40
- package/tools/{_deprecated/mute.js → mute.js} +4 -3
- package/tools/notifications.js +58 -48
- package/tools/observe.js +12 -15
- package/tools/onboarding.js +8 -11
- package/tools/open.js +13 -144
- package/tools/party-game.js +23 -12
- package/tools/patterns.js +2 -1
- package/tools/ping.js +5 -7
- package/tools/react.js +28 -30
- package/tools/{_deprecated/recall.js → recall.js} +5 -10
- package/tools/release.js +4 -2
- package/tools/{_deprecated/remember.js → remember.js} +4 -6
- package/tools/report.js +2 -2
- package/tools/request.js +6 -26
- package/tools/reserve.js +1 -1
- package/tools/session-fork.js +97 -0
- package/tools/session-save.js +109 -0
- package/tools/settings.js +30 -99
- package/tools/ship.js +74 -56
- package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
- package/tools/social-inbox.js +22 -28
- package/tools/social-post.js +24 -27
- package/tools/solo-game.js +54 -46
- package/tools/start.js +14 -148
- package/tools/status.js +21 -68
- package/tools/submit.js +4 -2
- package/tools/suggest-tags.js +36 -33
- package/tools/summarize.js +19 -16
- package/tools/tag-suggestions.js +72 -73
- package/tools/test.js +1 -1
- package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
- package/tools/token.js +4 -4
- package/tools/update.js +1 -2
- package/tools/watch.js +132 -112
- package/tools/who.js +20 -40
- package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
- package/tools/workshop-buddy.js +52 -53
- package/tools/x-mentions.js +0 -1
- package/tools/x-reply.js +0 -1
- package/twitter.js +14 -20
- package/version.json +8 -10
- package/webhook-runner.js +132 -0
- package/auth-store.js +0 -148
- package/bridges/bridge-monitor.js +0 -388
- package/bridges/discord-bot.js +0 -431
- package/bridges/farcaster.js +0 -299
- package/bridges/telegram.js +0 -261
- package/bridges/webhook-health.js +0 -420
- package/bridges/webhook-server.js +0 -437
- package/bridges/whatsapp.js +0 -441
- package/bridges/x-webhook.js +0 -423
- package/games/arcade.js +0 -406
- package/games/chess.js +0 -451
- package/games/colorguess.js +0 -343
- package/games/crossword-words.js +0 -171
- package/games/crossword.js +0 -461
- package/games/drawing.js +0 -347
- package/games/gameroulette.js +0 -300
- package/games/gamerouter.js +0 -336
- package/games/gamestatus.js +0 -337
- package/games/guessnumber.js +0 -209
- package/games/hangman.js +0 -279
- package/games/memory.js +0 -338
- package/games/multiplayer-tictactoe.js +0 -389
- package/games/pixelart.js +0 -399
- package/games/quickduel.js +0 -354
- package/games/riddle.js +0 -371
- package/games/rockpaperscissors.js +0 -291
- package/games/snake.js +0 -406
- package/games/storybuilder.js +0 -343
- package/games/tictactoe.js +0 -345
- package/games/twentyquestions.js +0 -286
- package/games/twotruths.js +0 -207
- package/games/werewolf.js +0 -508
- package/games/wordassociation.js +0 -247
- package/games/wordchain.js +0 -135
- package/intelligence/interests.js +0 -369
- package/setup.js +0 -480
- package/smart-inbox.js +0 -276
- package/tools/_deprecated/auto-suggest-connections.js +0 -304
- package/tools/_deprecated/bootstrap-skills.js +0 -231
- package/tools/_deprecated/bridge-dashboard.js +0 -342
- package/tools/_deprecated/bridge-health.js +0 -400
- package/tools/_deprecated/bridge-live.js +0 -384
- package/tools/_deprecated/bridges.js +0 -383
- package/tools/_deprecated/colorguess.js +0 -281
- package/tools/_deprecated/discover-insights.js +0 -379
- package/tools/_deprecated/discover-momentum.js +0 -256
- package/tools/_deprecated/discovery-analytics.js +0 -345
- package/tools/_deprecated/discovery-auto-suggest.js +0 -275
- package/tools/_deprecated/discovery-bootstrap.js +0 -267
- package/tools/_deprecated/discovery-daily.js +0 -375
- package/tools/_deprecated/discovery-dashboard.js +0 -385
- package/tools/_deprecated/discovery-digest.js +0 -314
- package/tools/_deprecated/discovery-hub.js +0 -357
- package/tools/_deprecated/discovery-insights.js +0 -384
- package/tools/_deprecated/discovery-momentum.js +0 -281
- package/tools/_deprecated/discovery-monitor.js +0 -319
- package/tools/_deprecated/discovery-proactive.js +0 -300
- package/tools/_deprecated/draw.js +0 -317
- package/tools/_deprecated/farcaster.js +0 -307
- package/tools/_deprecated/games-catalog.js +0 -376
- package/tools/_deprecated/games.js +0 -313
- package/tools/_deprecated/guessnumber.js +0 -194
- package/tools/_deprecated/hangman.js +0 -129
- package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
- package/tools/_deprecated/riddle.js +0 -240
- package/tools/_deprecated/run-bootstrap.js +0 -69
- package/tools/_deprecated/skills-analytics.js +0 -349
- package/tools/_deprecated/skills-bootstrap.js +0 -301
- package/tools/_deprecated/skills-dashboard.js +0 -268
- package/tools/_deprecated/skills.js +0 -380
- package/tools/_deprecated/smart-intro.js +0 -353
- package/tools/_deprecated/storybuilder.js +0 -331
- package/tools/_deprecated/telegram-bot.js +0 -183
- package/tools/_deprecated/telegram-setup.js +0 -214
- package/tools/_deprecated/twentyquestions.js +0 -143
- package/tools/_shared.js +0 -234
- package/tools/_work-context.js +0 -338
- package/tools/_work-context.manual-test.js +0 -199
- package/tools/_work-context.test.js +0 -260
- package/tools/activity.js +0 -220
- package/tools/agent-treasury.js +0 -288
- package/tools/analytics.js +0 -191
- package/tools/approve.js +0 -197
- package/tools/arcade.js +0 -173
- package/tools/artifacts-price.js +0 -107
- package/tools/ask-expert.js +0 -160
- package/tools/available.js +0 -120
- package/tools/become-expert.js +0 -150
- package/tools/broadcast.js +0 -325
- package/tools/chat.js +0 -202
- package/tools/collaborative-drawing.js +0 -286
- package/tools/connection-status.js +0 -178
- package/tools/earnings.js +0 -126
- package/tools/friends.js +0 -207
- package/tools/genesis.js +0 -233
- package/tools/gig-browse.js +0 -206
- package/tools/gig-complete.js +0 -144
- package/tools/health.js +0 -87
- package/tools/leaderboard.js +0 -117
- package/tools/lib/git-apply.js +0 -206
- package/tools/lib/git-bundle.js +0 -407
- package/tools/mint.js +0 -377
- package/tools/plan.js +0 -225
- package/tools/profile.js +0 -219
- package/tools/proof-of-work.js +0 -144
- package/tools/pulse.js +0 -218
- package/tools/reply.js +0 -166
- package/tools/reputation.js +0 -175
- package/tools/schedule.js +0 -367
- package/tools/search-messages.js +0 -123
- package/tools/session.js +0 -467
- package/tools/session_price.js +0 -128
- package/tools/smart-check.js +0 -201
- package/tools/social-processor.js +0 -445
- package/tools/streak.js +0 -147
- package/tools/stuck.js +0 -297
- package/tools/subscribe.js +0 -148
- package/tools/subscriptions.js +0 -134
- package/tools/tip.js +0 -193
- package/tools/wallet.js +0 -269
- package/tools/webhook-test.js +0 -388
- package/tools/withdraw.js +0 -145
- package/tools/work-summary.js +0 -96
- package/tools/workshop.js +0 -327
- /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
- /package/tools/{l2.js → _experimental/l2.js} +0 -0
- /package/tools/{_deprecated/away.js → away.js} +0 -0
|
@@ -48,7 +48,7 @@ async function getGameState(myHandle) {
|
|
|
48
48
|
// This allows for multiplayer games that persist across sessions
|
|
49
49
|
const gameRoomHandle = 'wordassociation-room';
|
|
50
50
|
const thread = await store.getThread(myHandle, gameRoomHandle);
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
// Find the most recent game payload
|
|
53
53
|
for (let i = thread.length - 1; i >= 0; i--) {
|
|
54
54
|
const msg = thread[i];
|
|
@@ -56,7 +56,7 @@ async function getGameState(myHandle) {
|
|
|
56
56
|
return { gameState: msg.payload.state, thread };
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
return { gameState: null, thread };
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -66,7 +66,7 @@ async function getGameState(myHandle) {
|
|
|
66
66
|
async function saveGameState(myHandle, gameState, action) {
|
|
67
67
|
const gameRoomHandle = 'wordassociation-room';
|
|
68
68
|
const payload = createGamePayload('wordassociation', gameState);
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
let message;
|
|
71
71
|
if (action.type === 'new') {
|
|
72
72
|
message = 'Started a new word association game!';
|
|
@@ -80,7 +80,7 @@ async function saveGameState(myHandle, gameState, action) {
|
|
|
80
80
|
} else {
|
|
81
81
|
message = 'Game updated';
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
await store.sendMessage(myHandle, gameRoomHandle, message, 'dm', payload);
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -115,7 +115,7 @@ async function handler(args) {
|
|
|
115
115
|
if (newGame) {
|
|
116
116
|
const newState = wordassociation.createInitialWordAssociationState();
|
|
117
117
|
const addResult = wordassociation.addPlayer(newState, myHandle);
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
if (addResult.error) {
|
|
120
120
|
return { display: addResult.error };
|
|
121
121
|
}
|
|
@@ -133,7 +133,7 @@ async function handler(args) {
|
|
|
133
133
|
// Start a new game if none exists
|
|
134
134
|
const newState = wordassociation.createInitialWordAssociationState();
|
|
135
135
|
const addResult = wordassociation.addPlayer(newState, myHandle);
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
if (addResult.error) {
|
|
138
138
|
return { display: addResult.error };
|
|
139
139
|
}
|
|
@@ -150,7 +150,7 @@ async function handler(args) {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
const addResult = wordassociation.addPlayer(gameState, myHandle);
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
if (addResult.error) {
|
|
155
155
|
return { display: addResult.error };
|
|
156
156
|
}
|
|
@@ -165,7 +165,10 @@ async function handler(args) {
|
|
|
165
165
|
// Handle making a word move
|
|
166
166
|
if (word) {
|
|
167
167
|
if (!gameState) {
|
|
168
|
-
return {
|
|
168
|
+
return {
|
|
169
|
+
display:
|
|
170
|
+
'No active game! Use `vibe wordassociation --new` to start one, or `vibe wordassociation --join` to join.'
|
|
171
|
+
};
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
if (gameState.gameOver) {
|
|
@@ -174,24 +177,24 @@ async function handler(args) {
|
|
|
174
177
|
|
|
175
178
|
// Try to make the move
|
|
176
179
|
const moveResult = wordassociation.makeMove(gameState, word, myHandle);
|
|
177
|
-
|
|
180
|
+
|
|
178
181
|
if (moveResult.error) {
|
|
179
182
|
return { display: `❌ ${moveResult.error}` };
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
await saveGameState(myHandle, moveResult.gameState, {
|
|
183
|
-
type: 'word',
|
|
184
|
-
player: myHandle,
|
|
185
|
-
word: word
|
|
185
|
+
await saveGameState(myHandle, moveResult.gameState, {
|
|
186
|
+
type: 'word',
|
|
187
|
+
player: myHandle,
|
|
188
|
+
word: word
|
|
186
189
|
});
|
|
187
190
|
|
|
188
191
|
const display = wordassociation.formatWordAssociationDisplay(moveResult.gameState);
|
|
189
|
-
|
|
192
|
+
|
|
190
193
|
// Add fun themes if game is complete
|
|
191
194
|
if (moveResult.gameState.gameOver) {
|
|
192
195
|
const themes = wordassociation.findThemes(moveResult.gameState);
|
|
193
196
|
const stats = wordassociation.getGameStats(moveResult.gameState);
|
|
194
|
-
|
|
197
|
+
|
|
195
198
|
let extra = '\n\n**Final Stats:**\n';
|
|
196
199
|
if (stats) {
|
|
197
200
|
extra += `Total words: ${stats.totalWords}\n`;
|
|
@@ -200,7 +203,7 @@ async function handler(args) {
|
|
|
200
203
|
extra += `\n**Themes found:** ${themes.join(', ')}`;
|
|
201
204
|
}
|
|
202
205
|
}
|
|
203
|
-
|
|
206
|
+
|
|
204
207
|
return {
|
|
205
208
|
display: `🧠 **Word Association**\n\n${display}${extra}\n\nStart a new game with \`vibe wordassociation --new\`!`
|
|
206
209
|
};
|
|
@@ -214,11 +217,11 @@ async function handler(args) {
|
|
|
214
217
|
// Show current game state
|
|
215
218
|
if (gameState) {
|
|
216
219
|
const display = wordassociation.formatWordAssociationDisplay(gameState);
|
|
217
|
-
|
|
220
|
+
|
|
218
221
|
if (gameState.gameOver) {
|
|
219
222
|
const themes = wordassociation.findThemes(gameState);
|
|
220
223
|
const stats = wordassociation.getGameStats(gameState);
|
|
221
|
-
|
|
224
|
+
|
|
222
225
|
let extra = '\n\n**Final Stats:**\n';
|
|
223
226
|
if (stats) {
|
|
224
227
|
extra += `Total words: ${stats.totalWords}\n`;
|
|
@@ -227,7 +230,7 @@ async function handler(args) {
|
|
|
227
230
|
extra += `\n**Themes found:** ${themes.join(', ')}`;
|
|
228
231
|
}
|
|
229
232
|
}
|
|
230
|
-
|
|
233
|
+
|
|
231
234
|
return {
|
|
232
235
|
display: `🧠 **Word Association**\n\n${display}${extra}\n\nStart a new game with \`vibe wordassociation --new\`!`
|
|
233
236
|
};
|
|
@@ -244,4 +247,4 @@ async function handler(args) {
|
|
|
244
247
|
};
|
|
245
248
|
}
|
|
246
249
|
|
|
247
|
-
module.exports = { definition, handler };
|
|
250
|
+
module.exports = { definition, handler };
|
package/tools/workshop-buddy.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const config = require('../config');
|
|
16
|
-
const
|
|
16
|
+
const _store = require('../store');
|
|
17
17
|
const userProfiles = require('../store/profiles');
|
|
18
18
|
const { formatTimeAgo, requireInit } = require('./_shared');
|
|
19
19
|
|
|
@@ -38,26 +38,26 @@ const definition = {
|
|
|
38
38
|
|
|
39
39
|
// Skill complementarity matrix
|
|
40
40
|
const complementarySkills = {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
frontend: ['backend', 'design', 'ux'],
|
|
42
|
+
backend: ['frontend', 'devops', 'database'],
|
|
43
|
+
design: ['frontend', 'user-research', 'engineering'],
|
|
44
|
+
ux: ['design', 'frontend', 'user-research'],
|
|
45
|
+
ai: ['data', 'backend', 'research'],
|
|
46
|
+
data: ['ai', 'backend', 'analytics'],
|
|
47
|
+
mobile: ['backend', 'design', 'ux'],
|
|
48
|
+
devops: ['backend', 'security', 'infrastructure'],
|
|
49
|
+
product: ['engineering', 'design', 'marketing'],
|
|
50
|
+
engineering: ['product', 'design', 'backend'],
|
|
51
|
+
research: ['ai', 'data', 'implementation'],
|
|
52
|
+
marketing: ['product', 'design', 'content'],
|
|
53
|
+
content: ['marketing', 'design', 'writing'],
|
|
54
|
+
security: ['devops', 'backend', 'infrastructure'],
|
|
55
|
+
infrastructure: ['devops', 'backend', 'security'],
|
|
56
|
+
analytics: ['data', 'product', 'research'],
|
|
57
57
|
'user-research': ['ux', 'product', 'design'],
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
writing: ['content', 'marketing', 'editing'],
|
|
59
|
+
business: ['product', 'marketing', 'strategy'],
|
|
60
|
+
strategy: ['business', 'product', 'leadership']
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
// Calculate workshop collaboration potential
|
|
@@ -84,12 +84,12 @@ function calculateBuddyScore(user1, user2) {
|
|
|
84
84
|
if (user1.building && user2.building) {
|
|
85
85
|
const building1 = user1.building.toLowerCase();
|
|
86
86
|
const building2 = user2.building.toLowerCase();
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
// Different but related domains
|
|
89
89
|
const domains = ['ai', 'web', 'mobile', 'fintech', 'healthcare', 'gaming', 'productivity'];
|
|
90
90
|
const user1Domains = domains.filter(d => building1.includes(d));
|
|
91
91
|
const user2Domains = domains.filter(d => building2.includes(d));
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
if (user1Domains.length > 0 && user2Domains.length > 0) {
|
|
94
94
|
const sharedDomains = user1Domains.filter(d => user2Domains.includes(d));
|
|
95
95
|
if (sharedDomains.length > 0) {
|
|
@@ -102,7 +102,7 @@ function calculateBuddyScore(user1, user2) {
|
|
|
102
102
|
// Experience level balance
|
|
103
103
|
const user1Level = getExperienceLevel(user1);
|
|
104
104
|
const user2Level = getExperienceLevel(user2);
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
if (Math.abs(user1Level - user2Level) <= 1) {
|
|
107
107
|
score += 15;
|
|
108
108
|
reasons.push('Compatible experience levels');
|
|
@@ -131,7 +131,7 @@ function getExperienceLevel(user) {
|
|
|
131
131
|
const skills = (user.tags || []).length;
|
|
132
132
|
const interests = (user.interests || []).length;
|
|
133
133
|
const hasBuilding = user.building ? 1 : 0;
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
return Math.min(5, skills + interests + hasBuilding);
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -139,13 +139,14 @@ function getExperienceLevel(user) {
|
|
|
139
139
|
async function findWorkshopBuddies(myHandle) {
|
|
140
140
|
const myProfile = await userProfiles.getProfile(myHandle);
|
|
141
141
|
const allProfiles = await userProfiles.getAllProfiles();
|
|
142
|
-
|
|
142
|
+
|
|
143
143
|
const candidates = allProfiles.filter(p => p.handle !== myHandle);
|
|
144
144
|
const matches = [];
|
|
145
145
|
|
|
146
146
|
for (const candidate of candidates) {
|
|
147
147
|
const match = calculateBuddyScore(myProfile, candidate);
|
|
148
|
-
if (match.score > 20) {
|
|
148
|
+
if (match.score > 20) {
|
|
149
|
+
// Higher threshold for workshop partnerships
|
|
149
150
|
matches.push({
|
|
150
151
|
handle: candidate.handle,
|
|
151
152
|
score: match.score,
|
|
@@ -202,7 +203,7 @@ async function handler(args) {
|
|
|
202
203
|
switch (command) {
|
|
203
204
|
case 'find': {
|
|
204
205
|
const buddies = await findWorkshopBuddies(myHandle);
|
|
205
|
-
|
|
206
|
+
|
|
206
207
|
if (buddies.length === 0) {
|
|
207
208
|
display = `## No Workshop Buddies Found
|
|
208
209
|
|
|
@@ -220,27 +221,27 @@ _Need more profiles to find great partnerships._
|
|
|
220
221
|
- Shared domain interest`;
|
|
221
222
|
} else {
|
|
222
223
|
display = `## Your Workshop Buddy Matches 🤝\n\n`;
|
|
223
|
-
|
|
224
|
+
|
|
224
225
|
for (const buddy of buddies) {
|
|
225
226
|
display += `### **@${buddy.handle}** _(${buddy.score}% match)_\n`;
|
|
226
227
|
display += `${buddy.building || 'Available for collaboration'}\n\n`;
|
|
227
|
-
|
|
228
|
+
|
|
228
229
|
display += `**Why you'd work great together:**\n`;
|
|
229
230
|
for (const reason of buddy.reasons) {
|
|
230
231
|
display += `• ${reason}\n`;
|
|
231
232
|
}
|
|
232
|
-
|
|
233
|
+
|
|
233
234
|
if (buddy.collaborationPotential.length > 0) {
|
|
234
235
|
display += `\n**Skill synergy:** ${buddy.collaborationPotential.slice(0, 2).join(', ')}\n`;
|
|
235
236
|
}
|
|
236
|
-
|
|
237
|
+
|
|
237
238
|
display += `**Their skills:** ${buddy.tags.join(', ')}\n`;
|
|
238
239
|
display += `**Experience level:** ${'⭐'.repeat(buddy.experienceLevel)}\n`;
|
|
239
240
|
display += `_Last seen: ${formatTimeAgo(buddy.lastSeen)}_\n\n`;
|
|
240
241
|
display += `**Reach out:** \`message @${buddy.handle} "Hey! We seem like a great workshop match..."\`\n\n`;
|
|
241
242
|
display += '---\n\n';
|
|
242
243
|
}
|
|
243
|
-
|
|
244
|
+
|
|
244
245
|
display += `**Next steps:**\n`;
|
|
245
246
|
display += `• Message your top match to start collaborating\n`;
|
|
246
247
|
display += `• Use \`workshop-buddy matches\` to see community skill exchanges\n`;
|
|
@@ -251,16 +252,16 @@ _Need more profiles to find great partnerships._
|
|
|
251
252
|
|
|
252
253
|
case 'offer': {
|
|
253
254
|
if (!args.skills) {
|
|
254
|
-
return {
|
|
255
|
+
return { display: 'Specify skills you offer: workshop-buddy offer "frontend, react"' };
|
|
255
256
|
}
|
|
256
|
-
|
|
257
|
+
|
|
257
258
|
// Update user tags with offered skills
|
|
258
259
|
const skills = args.skills.split(',').map(s => s.trim().toLowerCase());
|
|
259
260
|
const currentProfile = await userProfiles.getProfile(myHandle);
|
|
260
261
|
const updatedTags = [...new Set([...(currentProfile.tags || []), ...skills])];
|
|
261
|
-
|
|
262
|
+
|
|
262
263
|
await userProfiles.updateProfile(myHandle, { tags: updatedTags });
|
|
263
|
-
|
|
264
|
+
|
|
264
265
|
display = `## Skills Offered! 🎯
|
|
265
266
|
|
|
266
267
|
**You're now offering:** ${skills.join(', ')}
|
|
@@ -276,17 +277,17 @@ People looking for these skills can find you via:
|
|
|
276
277
|
|
|
277
278
|
case 'seeking': {
|
|
278
279
|
if (!args.skills) {
|
|
279
|
-
return {
|
|
280
|
+
return { display: 'Specify skills you need: workshop-buddy seeking "backend, devops"' };
|
|
280
281
|
}
|
|
281
|
-
|
|
282
|
+
|
|
282
283
|
const seekingSkills = args.skills.split(',').map(s => s.trim().toLowerCase());
|
|
283
284
|
const allProfiles = await userProfiles.getAllProfiles();
|
|
284
|
-
|
|
285
|
+
|
|
285
286
|
const matches = allProfiles.filter(profile => {
|
|
286
287
|
const theirSkills = (profile.tags || []).map(t => t.toLowerCase());
|
|
287
288
|
return seekingSkills.some(skill => theirSkills.includes(skill)) && profile.handle !== myHandle;
|
|
288
289
|
});
|
|
289
|
-
|
|
290
|
+
|
|
290
291
|
if (matches.length === 0) {
|
|
291
292
|
display = `## No One Found with: ${args.skills}
|
|
292
293
|
|
|
@@ -296,19 +297,17 @@ People looking for these skills can find you via:
|
|
|
296
297
|
- Post what you're seeking: \`ship "Looking for ${args.skills} expertise"\``;
|
|
297
298
|
} else {
|
|
298
299
|
display = `## People with: ${args.skills}\n\n`;
|
|
299
|
-
|
|
300
|
+
|
|
300
301
|
for (const match of matches) {
|
|
301
|
-
const theirMatchingSkills = (match.tags || []).filter(t =>
|
|
302
|
-
|
|
303
|
-
);
|
|
304
|
-
|
|
302
|
+
const theirMatchingSkills = (match.tags || []).filter(t => seekingSkills.includes(t.toLowerCase()));
|
|
303
|
+
|
|
305
304
|
display += `**@${match.handle}**\n`;
|
|
306
305
|
display += `${match.building || 'Available to help'}\n`;
|
|
307
306
|
display += `**Has:** ${theirMatchingSkills.join(', ')}\n`;
|
|
308
307
|
display += `**All skills:** ${(match.tags || []).join(', ')}\n`;
|
|
309
308
|
display += `_Last seen: ${formatTimeAgo(match.lastSeen)}_\n\n`;
|
|
310
309
|
}
|
|
311
|
-
|
|
310
|
+
|
|
312
311
|
display += `**Ready to collaborate?**\n`;
|
|
313
312
|
display += `\`workshop-buddy find\` — See your best overall matches`;
|
|
314
313
|
}
|
|
@@ -317,7 +316,7 @@ People looking for these skills can find you via:
|
|
|
317
316
|
|
|
318
317
|
case 'matches': {
|
|
319
318
|
const exchanges = await getSkillExchange();
|
|
320
|
-
|
|
319
|
+
|
|
321
320
|
if (exchanges.length === 0) {
|
|
322
321
|
display = `## No Skill Exchanges Yet
|
|
323
322
|
|
|
@@ -333,7 +332,7 @@ _The skill marketplace is empty._
|
|
|
333
332
|
- Business: product, marketing, strategy, writing`;
|
|
334
333
|
} else {
|
|
335
334
|
display = `## Community Skill Exchange 🔄\n\n`;
|
|
336
|
-
|
|
335
|
+
|
|
337
336
|
const groupedByOffering = {};
|
|
338
337
|
for (const exchange of exchanges) {
|
|
339
338
|
if (!groupedByOffering[exchange.offering]) {
|
|
@@ -341,20 +340,20 @@ _The skill marketplace is empty._
|
|
|
341
340
|
}
|
|
342
341
|
groupedByOffering[exchange.offering].push(exchange);
|
|
343
342
|
}
|
|
344
|
-
|
|
343
|
+
|
|
345
344
|
for (const [skill, providers] of Object.entries(groupedByOffering)) {
|
|
346
345
|
display += `### ${skill.toUpperCase()}\n`;
|
|
347
|
-
|
|
346
|
+
|
|
348
347
|
for (const provider of providers.slice(0, 3)) {
|
|
349
348
|
display += `**@${provider.handle}** offers ${skill}, seeks: ${provider.seeking.join(', ')}\n`;
|
|
350
349
|
if (provider.building) {
|
|
351
350
|
display += `_Building: ${provider.building}_\n`;
|
|
352
351
|
}
|
|
353
352
|
}
|
|
354
|
-
|
|
353
|
+
|
|
355
354
|
display += '\n';
|
|
356
355
|
}
|
|
357
|
-
|
|
356
|
+
|
|
358
357
|
display += `**Find your perfect exchange:**\n`;
|
|
359
358
|
display += `- \`workshop-buddy seeking "skill"\` to find specific expertise\n`;
|
|
360
359
|
display += `- \`workshop-buddy find\` for AI-matched partners`;
|
|
@@ -391,4 +390,4 @@ Try: \`workshop-buddy\` for available commands`;
|
|
|
391
390
|
return { display };
|
|
392
391
|
}
|
|
393
392
|
|
|
394
|
-
module.exports = { definition, handler };
|
|
393
|
+
module.exports = { definition, handler };
|
package/tools/x-mentions.js
CHANGED
package/tools/x-reply.js
CHANGED
package/twitter.js
CHANGED
|
@@ -34,10 +34,7 @@ function generateOAuthSignature(method, url, params, consumerSecret, tokenSecret
|
|
|
34
34
|
|
|
35
35
|
const signingKey = `${encodeURIComponent(consumerSecret)}&${encodeURIComponent(tokenSecret || '')}`;
|
|
36
36
|
|
|
37
|
-
return crypto
|
|
38
|
-
.createHmac('sha1', signingKey)
|
|
39
|
-
.update(signatureBaseString)
|
|
40
|
-
.digest('base64');
|
|
37
|
+
return crypto.createHmac('sha1', signingKey).update(signatureBaseString).digest('base64');
|
|
41
38
|
}
|
|
42
39
|
|
|
43
40
|
/**
|
|
@@ -58,13 +55,7 @@ function generateOAuthHeader(method, url, params = {}) {
|
|
|
58
55
|
|
|
59
56
|
const allParams = { ...oauthParams, ...params };
|
|
60
57
|
|
|
61
|
-
oauthParams.oauth_signature = generateOAuthSignature(
|
|
62
|
-
method,
|
|
63
|
-
url,
|
|
64
|
-
allParams,
|
|
65
|
-
creds.api_secret,
|
|
66
|
-
creds.access_secret
|
|
67
|
-
);
|
|
58
|
+
oauthParams.oauth_signature = generateOAuthSignature(method, url, allParams, creds.api_secret, creds.access_secret);
|
|
68
59
|
|
|
69
60
|
const headerString = Object.keys(oauthParams)
|
|
70
61
|
.sort()
|
|
@@ -82,13 +73,11 @@ async function xRequest(method, endpoint, params = {}, body = null) {
|
|
|
82
73
|
const url = `${baseUrl}${endpoint}`;
|
|
83
74
|
|
|
84
75
|
const headers = {
|
|
85
|
-
|
|
76
|
+
Authorization: generateOAuthHeader(method, url, method === 'GET' ? params : {}),
|
|
86
77
|
'Content-Type': 'application/json'
|
|
87
78
|
};
|
|
88
79
|
|
|
89
|
-
const fetchUrl = method === 'GET' && Object.keys(params).length > 0
|
|
90
|
-
? `${url}?${new URLSearchParams(params)}`
|
|
91
|
-
: url;
|
|
80
|
+
const fetchUrl = method === 'GET' && Object.keys(params).length > 0 ? `${url}?${new URLSearchParams(params)}` : url;
|
|
92
81
|
|
|
93
82
|
const response = await fetch(fetchUrl, {
|
|
94
83
|
method,
|
|
@@ -126,7 +115,7 @@ async function getMentions(sinceId = null) {
|
|
|
126
115
|
|
|
127
116
|
const params = {
|
|
128
117
|
'tweet.fields': 'created_at,author_id,text,conversation_id',
|
|
129
|
-
|
|
118
|
+
expansions: 'author_id',
|
|
130
119
|
'user.fields': 'username,name,profile_image_url',
|
|
131
120
|
max_results: '10'
|
|
132
121
|
};
|
|
@@ -144,7 +133,7 @@ async function getMentions(sinceId = null) {
|
|
|
144
133
|
async function getDMConversations() {
|
|
145
134
|
return xRequest('GET', '/2/dm_conversations', {
|
|
146
135
|
'dm_event.fields': 'created_at,sender_id,text',
|
|
147
|
-
|
|
136
|
+
expansions: 'sender_id',
|
|
148
137
|
'user.fields': 'username,name'
|
|
149
138
|
});
|
|
150
139
|
}
|
|
@@ -162,9 +151,14 @@ async function getDMEvents(conversationId) {
|
|
|
162
151
|
* Send a DM
|
|
163
152
|
*/
|
|
164
153
|
async function sendDM(recipientId, text) {
|
|
165
|
-
return xRequest(
|
|
166
|
-
|
|
167
|
-
|
|
154
|
+
return xRequest(
|
|
155
|
+
'POST',
|
|
156
|
+
'/2/dm_conversations/with/' + recipientId + '/messages',
|
|
157
|
+
{},
|
|
158
|
+
{
|
|
159
|
+
text
|
|
160
|
+
}
|
|
161
|
+
);
|
|
168
162
|
}
|
|
169
163
|
|
|
170
164
|
/**
|
package/version.json
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.3.
|
|
3
|
-
"updated": "2026-01-
|
|
4
|
-
"changelog": "
|
|
2
|
+
"version": "0.3.0",
|
|
3
|
+
"updated": "2026-01-10",
|
|
4
|
+
"changelog": "Artifacts system - create guides, learnings, workspaces with vibe_create_artifact",
|
|
5
5
|
"features": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"Gig recommendations based on proven skills",
|
|
12
|
-
"Mobile dashboard API for fast loading"
|
|
6
|
+
"vibe_create_artifact - Create social artifacts from conversations",
|
|
7
|
+
"vibe_view_artifact - View and list artifacts",
|
|
8
|
+
"Dual-write to KV + Postgres",
|
|
9
|
+
"HTML rendering at /a/:slug",
|
|
10
|
+
"Structured blocks: places, schedules, checklists, callouts"
|
|
13
11
|
],
|
|
14
12
|
"breaking": false,
|
|
15
13
|
"updateUrl": "https://www.slashvibe.dev/api/version"
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* /vibe Webhook Server Runner
|
|
5
|
+
*
|
|
6
|
+
* Standalone webhook server for receiving real-time updates from external platforms.
|
|
7
|
+
* Run this on a public server to bridge external platforms into /vibe.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node webhook-runner.js [--port 3001]
|
|
11
|
+
*
|
|
12
|
+
* Environment Variables:
|
|
13
|
+
* WEBHOOK_PORT=3001
|
|
14
|
+
* WEBHOOK_SECRET=your-webhook-secret
|
|
15
|
+
* TELEGRAM_WEBHOOK_SECRET=telegram-secret-token
|
|
16
|
+
* DISCORD_PUBLIC_KEY=discord-public-key
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const express = require('express');
|
|
20
|
+
const cors = require('cors');
|
|
21
|
+
const { createWebhookHandler, getConfig, getSetupInstructions } = require('./bridges/webhook-server');
|
|
22
|
+
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
// Middleware
|
|
26
|
+
app.use(cors());
|
|
27
|
+
app.use(express.json({ limit: '10mb' }));
|
|
28
|
+
app.use(express.raw({ type: 'application/json', limit: '10mb' }));
|
|
29
|
+
|
|
30
|
+
// Health check
|
|
31
|
+
app.get('/health', (req, res) => {
|
|
32
|
+
res.json({
|
|
33
|
+
status: 'ok',
|
|
34
|
+
service: 'vibe-webhook-server',
|
|
35
|
+
timestamp: new Date().toISOString()
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Setup info endpoint
|
|
40
|
+
app.get('/setup', (req, res) => {
|
|
41
|
+
const instructions = getSetupInstructions();
|
|
42
|
+
res.json({
|
|
43
|
+
service: '/vibe Webhook Server',
|
|
44
|
+
instructions,
|
|
45
|
+
endpoints: {
|
|
46
|
+
telegram: '/webhook/telegram',
|
|
47
|
+
discord: '/webhook/discord',
|
|
48
|
+
health: '/health'
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Webhook endpoints
|
|
54
|
+
app.use('/webhook', createWebhookHandler());
|
|
55
|
+
|
|
56
|
+
// Error handler
|
|
57
|
+
app.use((err, req, res, next) => {
|
|
58
|
+
console.error('Webhook server error:', err);
|
|
59
|
+
res.status(500).json({
|
|
60
|
+
error: 'Internal server error',
|
|
61
|
+
timestamp: new Date().toISOString()
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Start server
|
|
66
|
+
function startServer() {
|
|
67
|
+
const config = getConfig();
|
|
68
|
+
const port = config.port;
|
|
69
|
+
|
|
70
|
+
const server = app.listen(port, () => {
|
|
71
|
+
console.log(`🌉 /vibe webhook server running on port ${port}`);
|
|
72
|
+
console.log();
|
|
73
|
+
console.log('📡 Endpoints:');
|
|
74
|
+
console.log(` Health: http://localhost:${port}/health`);
|
|
75
|
+
console.log(` Setup: http://localhost:${port}/setup`);
|
|
76
|
+
console.log(` Telegram: http://localhost:${port}/webhook/telegram`);
|
|
77
|
+
console.log(` Discord: http://localhost:${port}/webhook/discord`);
|
|
78
|
+
console.log();
|
|
79
|
+
console.log('🔧 Configuration:');
|
|
80
|
+
console.log(` Port: ${port}`);
|
|
81
|
+
console.log(` Webhook Secret: ${config.secret ? '✅ Set' : '❌ Not set'}`);
|
|
82
|
+
console.log(` Telegram Secret: ${config.telegramSecret ? '✅ Set' : '❌ Not set'}`);
|
|
83
|
+
console.log(` Discord Public Key: ${config.discordPublicKey ? '✅ Set' : '❌ Not set'}`);
|
|
84
|
+
console.log();
|
|
85
|
+
console.log('Visit /setup endpoint for platform-specific setup instructions.');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Graceful shutdown
|
|
89
|
+
process.on('SIGINT', () => {
|
|
90
|
+
console.log('\n🛑 Shutting down webhook server...');
|
|
91
|
+
server.close(() => {
|
|
92
|
+
console.log('✅ Webhook server stopped');
|
|
93
|
+
process.exit(0);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return server;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// CLI handling
|
|
101
|
+
if (require.main === module) {
|
|
102
|
+
const args = process.argv.slice(2);
|
|
103
|
+
|
|
104
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
105
|
+
console.log(`
|
|
106
|
+
/vibe Webhook Server
|
|
107
|
+
|
|
108
|
+
Usage:
|
|
109
|
+
node webhook-runner.js [options]
|
|
110
|
+
|
|
111
|
+
Options:
|
|
112
|
+
--port PORT Webhook server port (default: 3001)
|
|
113
|
+
--help, -h Show this help
|
|
114
|
+
|
|
115
|
+
Environment Variables:
|
|
116
|
+
WEBHOOK_PORT Webhook server port
|
|
117
|
+
WEBHOOK_SECRET Secret for webhook signature verification
|
|
118
|
+
TELEGRAM_WEBHOOK_SECRET Telegram webhook secret token
|
|
119
|
+
DISCORD_PUBLIC_KEY Discord application public key
|
|
120
|
+
|
|
121
|
+
Examples:
|
|
122
|
+
node webhook-runner.js
|
|
123
|
+
node webhook-runner.js --port 8080
|
|
124
|
+
WEBHOOK_PORT=3001 node webhook-runner.js
|
|
125
|
+
`);
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
startServer();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = { app, startServer };
|