slashvibe-mcp 0.3.21 → 0.3.22
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/config.js +36 -31
- package/crypto.js +1 -6
- package/discord.js +19 -19
- 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/notify.js +39 -14
- package/package.json +27 -20
- 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/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/analytics.js +0 -107
- package/auth-store.js +0 -148
- package/auto-update.js +0 -130
- 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/notification-emitter.js +0 -77
- 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
package/tools/discover.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* vibe discover — Find your people
|
|
3
3
|
*
|
|
4
4
|
* Smart matchmaking based on:
|
|
5
|
-
* - Social connections (X/GitHub mutual follows)
|
|
6
5
|
* - What you're building (similar projects)
|
|
7
6
|
* - What you've shipped (complementary skills)
|
|
8
7
|
* - When you're active (timezone overlap)
|
|
@@ -13,152 +12,66 @@
|
|
|
13
12
|
* - discover search <query> — Find people building specific things
|
|
14
13
|
* - discover interests — Browse people by interest tags
|
|
15
14
|
* - discover active — Show who's building similar things right now
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* Falls back to local matching if API unavailable.
|
|
15
|
+
* - discover skills — Skills marketplace (absorbed from skills-exchange)
|
|
16
|
+
* - discover partner — Find workshop partner (absorbed from workshop-buddy)
|
|
19
17
|
*/
|
|
20
18
|
|
|
21
19
|
const config = require('../config');
|
|
22
20
|
const store = require('../store');
|
|
23
21
|
const userProfiles = require('../store/profiles');
|
|
24
22
|
const { formatTimeAgo, requireInit } = require('./_shared');
|
|
25
|
-
const { getTechTags, getTechOneLiner } = require('../lib/tech-detection');
|
|
26
|
-
|
|
27
|
-
// API configuration
|
|
28
|
-
const RELEVANCY_API = 'https://www.slashvibe.dev/api/relevancy';
|
|
29
|
-
const API_TIMEOUT = 5000; // 5 seconds
|
|
30
|
-
|
|
31
|
-
// Detect current project tech stack for better matching
|
|
32
|
-
let cachedTechStack = null;
|
|
33
|
-
let techStackCacheTime = 0;
|
|
34
|
-
const TECH_CACHE_TTL = 60000; // 1 minute cache
|
|
35
|
-
|
|
36
|
-
function getCurrentTechStack() {
|
|
37
|
-
const now = Date.now();
|
|
38
|
-
if (cachedTechStack && (now - techStackCacheTime) < TECH_CACHE_TTL) {
|
|
39
|
-
return cachedTechStack;
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
cachedTechStack = {
|
|
43
|
-
tags: getTechTags(),
|
|
44
|
-
oneLiner: getTechOneLiner(),
|
|
45
|
-
};
|
|
46
|
-
techStackCacheTime = now;
|
|
47
|
-
} catch (e) {
|
|
48
|
-
cachedTechStack = { tags: [], oneLiner: '' };
|
|
49
|
-
}
|
|
50
|
-
return cachedTechStack;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Fetch suggestions from Universal Relevancy API
|
|
55
|
-
* Uses social signals (GitHub mutuals, etc.) for better matching
|
|
56
|
-
*
|
|
57
|
-
* @param {string} handle - User's vibe handle
|
|
58
|
-
* @param {number} limit - Max results (default 5)
|
|
59
|
-
* @returns {Promise<{ matches: object[], tier: string, fromApi: boolean } | null>}
|
|
60
|
-
*/
|
|
61
|
-
async function fetchApiSuggestions(handle, limit = 5) {
|
|
62
|
-
try {
|
|
63
|
-
const controller = new AbortController();
|
|
64
|
-
const timeout = setTimeout(() => controller.abort(), API_TIMEOUT);
|
|
65
|
-
|
|
66
|
-
// Include current project tech stack for better matching
|
|
67
|
-
const techStack = getCurrentTechStack();
|
|
68
|
-
const techTags = techStack.tags.join(',');
|
|
69
23
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const response = await fetch(url, {
|
|
76
|
-
method: 'GET',
|
|
77
|
-
headers: {
|
|
78
|
-
'Accept': 'application/json',
|
|
79
|
-
'User-Agent': 'vibe-mcp-client'
|
|
80
|
-
},
|
|
81
|
-
signal: controller.signal
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
clearTimeout(timeout);
|
|
85
|
-
|
|
86
|
-
if (!response.ok) {
|
|
87
|
-
console.log(`[discover] API returned ${response.status}`);
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const data = await response.json();
|
|
92
|
-
|
|
93
|
-
if (!data.success || !data.matches) {
|
|
94
|
-
console.log('[discover] API returned unsuccessful response');
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
matches: data.matches,
|
|
100
|
-
tier: data.tier,
|
|
101
|
-
socialStats: data.socialStats,
|
|
102
|
-
fromApi: true
|
|
103
|
-
};
|
|
104
|
-
} catch (e) {
|
|
105
|
-
// Log but don't fail - we'll fallback to local
|
|
106
|
-
if (e.name === 'AbortError') {
|
|
107
|
-
console.log('[discover] API timeout, falling back to local');
|
|
108
|
-
} else {
|
|
109
|
-
console.log('[discover] API error:', e.message);
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
24
|
+
// Delegate handlers for absorbed tools
|
|
25
|
+
const skillsExchangeTool = require('./skills-exchange');
|
|
26
|
+
const workshopBuddyTool = require('./workshop-buddy');
|
|
114
27
|
|
|
115
28
|
const definition = {
|
|
116
29
|
name: 'vibe_discover',
|
|
117
|
-
description:
|
|
30
|
+
description:
|
|
31
|
+
'Find people, skills, and partners. Commands: suggest, search, interests, active, skills (marketplace), partner (workshop buddy).',
|
|
118
32
|
inputSchema: {
|
|
119
33
|
type: 'object',
|
|
120
34
|
properties: {
|
|
121
35
|
command: {
|
|
122
36
|
type: 'string',
|
|
123
|
-
enum: ['suggest', 'search', 'interests', 'active', '
|
|
124
|
-
description: 'Discovery command
|
|
37
|
+
enum: ['suggest', 'search', 'interests', 'active', 'skills', 'partner'],
|
|
38
|
+
description: 'Discovery command to run'
|
|
125
39
|
},
|
|
126
40
|
query: {
|
|
127
41
|
type: 'string',
|
|
128
42
|
description: 'Search query (for search command)'
|
|
43
|
+
},
|
|
44
|
+
skills_command: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
enum: ['post', 'browse', 'match', 'requests'],
|
|
47
|
+
description: 'Skills subcommand (for command=skills)'
|
|
48
|
+
},
|
|
49
|
+
partner_command: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
enum: ['find', 'offer', 'seeking', 'matches'],
|
|
52
|
+
description: 'Partner subcommand (for command=partner)'
|
|
53
|
+
},
|
|
54
|
+
type: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
enum: ['offer', 'request'],
|
|
57
|
+
description: 'Type for skills post'
|
|
58
|
+
},
|
|
59
|
+
skill: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'Skill name (for skills command)'
|
|
62
|
+
},
|
|
63
|
+
skills: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Skills to offer/seek (for partner command)'
|
|
66
|
+
},
|
|
67
|
+
details: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
description: 'Additional details'
|
|
129
70
|
}
|
|
130
71
|
}
|
|
131
72
|
}
|
|
132
73
|
};
|
|
133
74
|
|
|
134
|
-
// Format duration in seconds to human readable
|
|
135
|
-
function formatDuration(seconds) {
|
|
136
|
-
if (!seconds || seconds < 60) return '<1m';
|
|
137
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
138
|
-
const hours = Math.floor(seconds / 3600);
|
|
139
|
-
const mins = Math.floor((seconds % 3600) / 60);
|
|
140
|
-
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Format scheduled time to relative string
|
|
144
|
-
function formatScheduledTime(isoString) {
|
|
145
|
-
const date = new Date(isoString);
|
|
146
|
-
const now = new Date();
|
|
147
|
-
const diffMs = date - now;
|
|
148
|
-
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
149
|
-
const diffDays = Math.floor(diffHours / 24);
|
|
150
|
-
|
|
151
|
-
if (diffMs < 0) return 'started';
|
|
152
|
-
if (diffDays === 0) {
|
|
153
|
-
if (diffHours <= 1) return 'in < 1 hour';
|
|
154
|
-
return `in ${diffHours} hours`;
|
|
155
|
-
} else if (diffDays === 1) {
|
|
156
|
-
return 'tomorrow';
|
|
157
|
-
} else {
|
|
158
|
-
return `in ${diffDays} days`;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
75
|
// Calculate match score between two users
|
|
163
76
|
function calculateMatchScore(user1, user2) {
|
|
164
77
|
let score = 0;
|
|
@@ -168,7 +81,7 @@ function calculateMatchScore(user1, user2) {
|
|
|
168
81
|
if (user1.building && user2.building) {
|
|
169
82
|
const building1 = user1.building.toLowerCase();
|
|
170
83
|
const building2 = user2.building.toLowerCase();
|
|
171
|
-
|
|
84
|
+
|
|
172
85
|
// Exact match
|
|
173
86
|
if (building1 === building2) {
|
|
174
87
|
score += 50;
|
|
@@ -253,9 +166,8 @@ function findComplementaryTags(tags1, tags2) {
|
|
|
253
166
|
return complementary;
|
|
254
167
|
}
|
|
255
168
|
|
|
256
|
-
// Get personalized suggestions
|
|
257
|
-
|
|
258
|
-
async function getLocalSuggestions(myHandle) {
|
|
169
|
+
// Get personalized suggestions
|
|
170
|
+
async function getSuggestions(myHandle) {
|
|
259
171
|
const myProfile = await userProfiles.getProfile(myHandle);
|
|
260
172
|
const allProfiles = await userProfiles.getAllProfiles();
|
|
261
173
|
|
|
@@ -264,7 +176,8 @@ async function getLocalSuggestions(myHandle) {
|
|
|
264
176
|
|
|
265
177
|
for (const candidate of candidates) {
|
|
266
178
|
const match = calculateMatchScore(myProfile, candidate);
|
|
267
|
-
if (match.score > 10) {
|
|
179
|
+
if (match.score > 10) {
|
|
180
|
+
// Minimum threshold
|
|
268
181
|
matches.push({
|
|
269
182
|
handle: candidate.handle,
|
|
270
183
|
score: match.score,
|
|
@@ -280,33 +193,11 @@ async function getLocalSuggestions(myHandle) {
|
|
|
280
193
|
return matches.sort((a, b) => b.score - a.score).slice(0, 5);
|
|
281
194
|
}
|
|
282
195
|
|
|
283
|
-
/**
|
|
284
|
-
* Get suggestions - tries API first, falls back to local
|
|
285
|
-
* @param {string} myHandle - User's handle
|
|
286
|
-
* @returns {Promise<{ matches: object[], fromApi: boolean, tier?: string }>}
|
|
287
|
-
*/
|
|
288
|
-
async function getSuggestions(myHandle) {
|
|
289
|
-
// Try API first (has social signals, better matching)
|
|
290
|
-
const apiResult = await fetchApiSuggestions(myHandle, 5);
|
|
291
|
-
|
|
292
|
-
if (apiResult && apiResult.matches.length > 0) {
|
|
293
|
-
return apiResult;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Fallback to local matching
|
|
297
|
-
const localMatches = await getLocalSuggestions(myHandle);
|
|
298
|
-
return {
|
|
299
|
-
matches: localMatches,
|
|
300
|
-
fromApi: false,
|
|
301
|
-
tier: 'local'
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
196
|
// Search for people by query
|
|
306
197
|
async function searchPeople(query) {
|
|
307
198
|
const allProfiles = await userProfiles.getAllProfiles();
|
|
308
199
|
const searchTerm = query.toLowerCase();
|
|
309
|
-
|
|
200
|
+
|
|
310
201
|
const results = allProfiles.filter(profile => {
|
|
311
202
|
return (
|
|
312
203
|
profile.building?.toLowerCase().includes(searchTerm) ||
|
|
@@ -337,7 +228,7 @@ async function browseByInterests() {
|
|
|
337
228
|
|
|
338
229
|
// Sort interests by popularity
|
|
339
230
|
const sorted = Object.entries(interestMap)
|
|
340
|
-
.sort(([,a], [,b]) => b.length - a.length)
|
|
231
|
+
.sort(([, a], [, b]) => b.length - a.length)
|
|
341
232
|
.slice(0, 10);
|
|
342
233
|
|
|
343
234
|
return sorted;
|
|
@@ -347,15 +238,15 @@ async function browseByInterests() {
|
|
|
347
238
|
async function getActiveSimilar(myHandle) {
|
|
348
239
|
const myProfile = await userProfiles.getProfile(myHandle);
|
|
349
240
|
const activeUsers = await store.getActiveUsers();
|
|
350
|
-
|
|
241
|
+
|
|
351
242
|
const similar = [];
|
|
352
|
-
|
|
243
|
+
|
|
353
244
|
for (const user of activeUsers) {
|
|
354
245
|
if (user.handle === myHandle) continue;
|
|
355
|
-
|
|
246
|
+
|
|
356
247
|
const theirProfile = await userProfiles.getProfile(user.handle);
|
|
357
248
|
const match = calculateMatchScore(myProfile, theirProfile);
|
|
358
|
-
|
|
249
|
+
|
|
359
250
|
if (match.score > 5) {
|
|
360
251
|
similar.push({
|
|
361
252
|
...user,
|
|
@@ -376,84 +267,73 @@ async function handler(args) {
|
|
|
376
267
|
const myHandle = config.getHandle();
|
|
377
268
|
const command = args.command || 'suggest';
|
|
378
269
|
|
|
270
|
+
// Route to absorbed tools
|
|
271
|
+
if (command === 'skills') {
|
|
272
|
+
return skillsExchangeTool.handler({
|
|
273
|
+
command: args.skills_command || 'browse',
|
|
274
|
+
type: args.type,
|
|
275
|
+
skill: args.skill,
|
|
276
|
+
details: args.details
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
if (command === 'partner') {
|
|
280
|
+
return workshopBuddyTool.handler({
|
|
281
|
+
command: args.partner_command || 'find',
|
|
282
|
+
skills: args.skills
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
379
286
|
let display = '';
|
|
380
287
|
|
|
381
288
|
try {
|
|
382
289
|
switch (command) {
|
|
383
290
|
case 'suggest': {
|
|
384
|
-
const
|
|
385
|
-
const { matches, fromApi, tier } = result;
|
|
291
|
+
const suggestions = await getSuggestions(myHandle);
|
|
386
292
|
|
|
387
|
-
if (
|
|
293
|
+
if (suggestions.length === 0) {
|
|
388
294
|
display = `## No Matches Found
|
|
389
295
|
|
|
390
296
|
_Not enough people with profiles yet._
|
|
391
297
|
|
|
392
298
|
**Help us learn about you:**
|
|
393
|
-
- Set interests: \`vibe update interests "ai, startups, music"\`
|
|
299
|
+
- Set interests: \`vibe update interests "ai, startups, music"\`
|
|
394
300
|
- Tag your skills: \`vibe update tags "frontend, react, typescript"\`
|
|
395
301
|
- Share what you're building: \`vibe update building "AI chat app"\`
|
|
396
302
|
|
|
397
303
|
The more people share, the better our matches become!`;
|
|
398
304
|
} else {
|
|
399
|
-
|
|
400
|
-
if (tier === 'contextual' || tier === 'mixed') {
|
|
401
|
-
display = `## 🔍 Suggested Connections\n\n`;
|
|
402
|
-
} else if (tier === 'trending') {
|
|
403
|
-
display = `## 🔥 Trending Builders\n\n`;
|
|
404
|
-
} else if (tier === 'new_joiners') {
|
|
405
|
-
display = `## 👋 New to /vibe\n\n`;
|
|
406
|
-
} else {
|
|
407
|
-
display = `## People You Should Meet\n\n`;
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Show detected tech stack if any
|
|
411
|
-
const techStack = getCurrentTechStack();
|
|
412
|
-
if (techStack.oneLiner) {
|
|
413
|
-
display += `_Matching for: ${techStack.oneLiner}_\n\n`;
|
|
414
|
-
}
|
|
305
|
+
display = `## People You Should Meet\n\n`;
|
|
415
306
|
|
|
416
|
-
for (const match of
|
|
417
|
-
|
|
418
|
-
const tierBadge = match.tierEmoji ? ` ${match.tierEmoji}` : '';
|
|
419
|
-
display += `**@${match.handle}**${tierBadge}\n`;
|
|
307
|
+
for (const match of suggestions) {
|
|
308
|
+
display += `**@${match.handle}** _(${match.score} match)_\n`;
|
|
420
309
|
display += `${match.building || 'Building something interesting'}\n`;
|
|
421
310
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
for (let i = 0; i < match.reasons.length; i++) {
|
|
425
|
-
const prefix = i === match.reasons.length - 1 ? '└─' : '├─';
|
|
426
|
-
display += ` ${prefix} ${match.reasons[i]}\n`;
|
|
427
|
-
}
|
|
311
|
+
if (match.reasons.length > 0) {
|
|
312
|
+
display += `🔗 ${match.reasons.join(' • ')}\n`;
|
|
428
313
|
}
|
|
429
|
-
|
|
430
|
-
|
|
314
|
+
|
|
315
|
+
if (match.interests.length > 0) {
|
|
431
316
|
display += `💡 ${match.interests.slice(0, 3).join(', ')}\n`;
|
|
432
317
|
}
|
|
433
318
|
|
|
434
|
-
display +=
|
|
319
|
+
display += `_Last seen: ${formatTimeAgo(match.lastSeen)}_\n\n`;
|
|
435
320
|
}
|
|
436
321
|
|
|
437
322
|
display += `**Next steps:**\n`;
|
|
438
323
|
display += `- \`message @handle\` to reach out\n`;
|
|
439
324
|
display += `- \`discover active\` to see who's online now\n`;
|
|
440
325
|
display += `- \`discover search <topic>\` to find specific interests`;
|
|
441
|
-
|
|
442
|
-
// Debug info (only in dev)
|
|
443
|
-
if (process.env.VIBE_DEBUG === 'true') {
|
|
444
|
-
display += `\n\n_Source: ${fromApi ? 'API' : 'local'} | Tier: ${tier}_`;
|
|
445
|
-
}
|
|
446
326
|
}
|
|
447
327
|
break;
|
|
448
328
|
}
|
|
449
329
|
|
|
450
330
|
case 'search': {
|
|
451
331
|
if (!args.query) {
|
|
452
|
-
return {
|
|
332
|
+
return { display: 'Please provide a search query: discover search "ai"' };
|
|
453
333
|
}
|
|
454
|
-
|
|
334
|
+
|
|
455
335
|
const results = await searchPeople(args.query);
|
|
456
|
-
|
|
336
|
+
|
|
457
337
|
if (results.length === 0) {
|
|
458
338
|
display = `## No Results for "${args.query}"
|
|
459
339
|
|
|
@@ -465,15 +345,15 @@ Try searching for:
|
|
|
465
345
|
Or browse by interest: \`discover interests\``;
|
|
466
346
|
} else {
|
|
467
347
|
display = `## People Building: "${args.query}"\n\n`;
|
|
468
|
-
|
|
348
|
+
|
|
469
349
|
for (const person of results) {
|
|
470
350
|
display += `**@${person.handle}**\n`;
|
|
471
351
|
display += `${person.building || 'Building something'}\n`;
|
|
472
|
-
|
|
352
|
+
|
|
473
353
|
if (person.tags && person.tags.length > 0) {
|
|
474
354
|
display += `🏷️ ${person.tags.join(', ')}\n`;
|
|
475
355
|
}
|
|
476
|
-
|
|
356
|
+
|
|
477
357
|
display += `_Last active: ${formatTimeAgo(person.lastSeen)}_\n\n`;
|
|
478
358
|
}
|
|
479
359
|
}
|
|
@@ -482,7 +362,7 @@ Or browse by interest: \`discover interests\``;
|
|
|
482
362
|
|
|
483
363
|
case 'interests': {
|
|
484
364
|
const interests = await browseByInterests();
|
|
485
|
-
|
|
365
|
+
|
|
486
366
|
if (interests.length === 0) {
|
|
487
367
|
display = `## No Interest Data Yet
|
|
488
368
|
|
|
@@ -492,16 +372,19 @@ People haven't shared their interests yet.
|
|
|
492
372
|
\`vibe update interests "ai, startups, music"\``;
|
|
493
373
|
} else {
|
|
494
374
|
display = `## Browse by Interest\n\n`;
|
|
495
|
-
|
|
375
|
+
|
|
496
376
|
for (const [interest, people] of interests) {
|
|
497
377
|
display += `**${interest}** (${people.length}): `;
|
|
498
|
-
display += people
|
|
378
|
+
display += people
|
|
379
|
+
.slice(0, 5)
|
|
380
|
+
.map(p => `@${p.handle}`)
|
|
381
|
+
.join(', ');
|
|
499
382
|
if (people.length > 5) {
|
|
500
383
|
display += ` +${people.length - 5} more`;
|
|
501
384
|
}
|
|
502
385
|
display += '\n\n';
|
|
503
386
|
}
|
|
504
|
-
|
|
387
|
+
|
|
505
388
|
display += `**Search for specific interest:**\n`;
|
|
506
389
|
display += `\`discover search "machine learning"\``;
|
|
507
390
|
}
|
|
@@ -518,7 +401,7 @@ No one with similar interests is active right now.
|
|
|
518
401
|
|
|
519
402
|
**Try:**
|
|
520
403
|
- \`who\` to see who's around
|
|
521
|
-
- \`discover suggest\` for general recommendations
|
|
404
|
+
- \`discover suggest\` for general recommendations
|
|
522
405
|
- \`discover search <topic>\` to find people by interest`;
|
|
523
406
|
} else {
|
|
524
407
|
display = `## Similar Builders Online Now\n\n`;
|
|
@@ -539,162 +422,17 @@ No one with similar interests is active right now.
|
|
|
539
422
|
break;
|
|
540
423
|
}
|
|
541
424
|
|
|
542
|
-
case 'live': {
|
|
543
|
-
// Fetch live broadcasts from discover API
|
|
544
|
-
const apiUrl = config.getApiUrl();
|
|
545
|
-
const response = await fetch(`${apiUrl}/api/discover?section=live`);
|
|
546
|
-
const data = await response.json();
|
|
547
|
-
|
|
548
|
-
if (!data.success || !data.live || data.live.length === 0) {
|
|
549
|
-
display = `## No One Live Right Now
|
|
550
|
-
|
|
551
|
-
_Check back later or schedule your own session!_
|
|
552
|
-
|
|
553
|
-
**Go live:** \`vibe broadcast start "What you're building"\`
|
|
554
|
-
**See upcoming:** \`vibe discover upcoming\``;
|
|
555
|
-
} else {
|
|
556
|
-
display = `## 🔴 Live Now\n\n`;
|
|
557
|
-
|
|
558
|
-
for (const broadcast of data.live) {
|
|
559
|
-
const duration = formatDuration(broadcast.durationSeconds);
|
|
560
|
-
display += `**@${broadcast.handle}** — ${broadcast.title}\n`;
|
|
561
|
-
display += `👥 ${broadcast.viewerCount} watching · ⏱️ ${duration}`;
|
|
562
|
-
if (broadcast.reactionCount > 0) {
|
|
563
|
-
display += ` · 🔥 ${broadcast.reactionCount}`;
|
|
564
|
-
}
|
|
565
|
-
display += `\n`;
|
|
566
|
-
display += `[Watch](${broadcast.watchUrl})\n\n`;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
display += `---\n`;
|
|
570
|
-
display += `\`vibe watch @handle\` to tune in`;
|
|
571
|
-
}
|
|
572
|
-
break;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
case 'upcoming': {
|
|
576
|
-
const apiUrl = config.getApiUrl();
|
|
577
|
-
const response = await fetch(`${apiUrl}/api/discover?section=scheduled`);
|
|
578
|
-
const data = await response.json();
|
|
579
|
-
|
|
580
|
-
if (!data.success || !data.scheduled || data.scheduled.length === 0) {
|
|
581
|
-
display = `## No Scheduled Sessions
|
|
582
|
-
|
|
583
|
-
_No one has scheduled a session yet._
|
|
584
|
-
|
|
585
|
-
**Schedule yours:** \`vibe schedule "Topic" --time "tomorrow 3pm"\`
|
|
586
|
-
**See who's live:** \`vibe discover live\``;
|
|
587
|
-
} else {
|
|
588
|
-
display = `## 📅 Upcoming Sessions\n\n`;
|
|
589
|
-
|
|
590
|
-
for (const session of data.scheduled) {
|
|
591
|
-
const when = formatScheduledTime(session.scheduledFor);
|
|
592
|
-
display += `**${session.title}**\n`;
|
|
593
|
-
display += `@${session.handle} · ${when}`;
|
|
594
|
-
if (session.rsvpCount > 0) {
|
|
595
|
-
display += ` · ${session.rsvpCount} RSVPs`;
|
|
596
|
-
}
|
|
597
|
-
display += `\n`;
|
|
598
|
-
if (session.tags && session.tags.length > 0) {
|
|
599
|
-
display += `🏷️ ${session.tags.join(', ')}\n`;
|
|
600
|
-
}
|
|
601
|
-
display += `\n`;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
display += `---\n`;
|
|
605
|
-
display += `\`vibe schedule rsvp <id>\` to get notified`;
|
|
606
|
-
}
|
|
607
|
-
break;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
case 'sessions': {
|
|
611
|
-
const apiUrl = config.getApiUrl();
|
|
612
|
-
const response = await fetch(`${apiUrl}/api/discover?section=sessions`);
|
|
613
|
-
const data = await response.json();
|
|
614
|
-
|
|
615
|
-
if (!data.success || !data.recentSessions || data.recentSessions.length === 0) {
|
|
616
|
-
display = `## No Recent Sessions
|
|
617
|
-
|
|
618
|
-
_No recorded sessions yet._
|
|
619
|
-
|
|
620
|
-
**Record yours:** After broadcasting, use \`vibe session save\``;
|
|
621
|
-
} else {
|
|
622
|
-
display = `## 📼 Recent Sessions\n\n`;
|
|
623
|
-
|
|
624
|
-
for (const session of data.recentSessions) {
|
|
625
|
-
display += `**${session.title}**\n`;
|
|
626
|
-
display += `@${session.handle}`;
|
|
627
|
-
if (session.views > 0) {
|
|
628
|
-
display += ` · ${session.views} views`;
|
|
629
|
-
}
|
|
630
|
-
if (session.duration) {
|
|
631
|
-
display += ` · ${formatDuration(session.duration)}`;
|
|
632
|
-
}
|
|
633
|
-
display += `\n`;
|
|
634
|
-
if (session.tags && session.tags.length > 0) {
|
|
635
|
-
display += `🏷️ ${session.tags.join(', ')}\n`;
|
|
636
|
-
}
|
|
637
|
-
display += `\n`;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
display += `---\n`;
|
|
641
|
-
display += `\`vibe session view <id>\` to watch`;
|
|
642
|
-
}
|
|
643
|
-
break;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
case 'creators': {
|
|
647
|
-
const apiUrl = config.getApiUrl();
|
|
648
|
-
const response = await fetch(`${apiUrl}/api/discover?handle=${myHandle}§ion=recommended`);
|
|
649
|
-
const data = await response.json();
|
|
650
|
-
|
|
651
|
-
if (!data.success || !data.recommended || data.recommended.length === 0) {
|
|
652
|
-
display = `## Recommended Creators
|
|
653
|
-
|
|
654
|
-
_Not enough data yet to make recommendations._
|
|
655
|
-
|
|
656
|
-
**Try:**
|
|
657
|
-
- \`vibe discover suggest\` — Find people by interests
|
|
658
|
-
- \`vibe discover active\` — See who's online now`;
|
|
659
|
-
} else {
|
|
660
|
-
display = `## 👤 Recommended Creators\n\n`;
|
|
661
|
-
|
|
662
|
-
for (const creator of data.recommended) {
|
|
663
|
-
display += `**@${creator.handle}**`;
|
|
664
|
-
if (creator.followerCount > 0) {
|
|
665
|
-
display += ` (${creator.followerCount} followers)`;
|
|
666
|
-
}
|
|
667
|
-
display += `\n`;
|
|
668
|
-
if (creator.building) {
|
|
669
|
-
display += `${creator.building}\n`;
|
|
670
|
-
}
|
|
671
|
-
display += `_${creator.reason}_\n\n`;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
display += `---\n`;
|
|
675
|
-
display += `\`vibe follow @handle\` to follow`;
|
|
676
|
-
}
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
425
|
default:
|
|
681
426
|
display = `## Discovery Commands
|
|
682
427
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
**Find People:**
|
|
689
|
-
- \`discover suggest\` — Get personalized recommendations
|
|
690
|
-
- \`discover creators\` — Popular creators to follow
|
|
691
|
-
- \`discover search <query>\` — Find people building specific things
|
|
692
|
-
- \`discover interests\` — Browse people by interest tags
|
|
693
|
-
- \`discover active\` — Who's building similar things right now
|
|
428
|
+
**\`discover suggest\`** — Get personalized recommendations
|
|
429
|
+
**\`discover search <query>\`** — Find people building specific things
|
|
430
|
+
**\`discover interests\`** — Browse people by interest tags
|
|
431
|
+
**\`discover active\`** — Show who's building similar things right now
|
|
694
432
|
|
|
695
433
|
**Set up your profile:**
|
|
696
434
|
- \`vibe update building "what you're working on"\`
|
|
697
|
-
- \`vibe update interests "ai, startups, music"\`
|
|
435
|
+
- \`vibe update interests "ai, startups, music"\`
|
|
698
436
|
- \`vibe update tags "frontend, react, typescript"\``;
|
|
699
437
|
}
|
|
700
438
|
} catch (error) {
|
|
@@ -708,4 +446,4 @@ Try: \`discover\` for available commands`;
|
|
|
708
446
|
return { display };
|
|
709
447
|
}
|
|
710
448
|
|
|
711
|
-
module.exports = { definition, handler };
|
|
449
|
+
module.exports = { definition, handler };
|