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.
Files changed (229) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/config.js +36 -31
  4. package/crypto.js +1 -6
  5. package/discord.js +19 -19
  6. package/index.js +217 -207
  7. package/intelligence/index.js +2 -9
  8. package/intelligence/infer.js +10 -16
  9. package/intelligence/patterns.js +23 -18
  10. package/intelligence/proactive.js +16 -15
  11. package/intelligence/serendipity.js +57 -20
  12. package/memory.js +13 -8
  13. package/notify.js +39 -14
  14. package/package.json +27 -20
  15. package/presence.js +2 -2
  16. package/prompts.js +5 -9
  17. package/protocol/index.js +123 -87
  18. package/protocol/telegram-commands.js +36 -37
  19. package/store/api.js +358 -529
  20. package/store/local.js +9 -10
  21. package/store/profiles.js +48 -192
  22. package/store/reservations.js +2 -9
  23. package/store/skills.js +69 -71
  24. package/store/sqlite.js +355 -0
  25. package/tools/_actions.js +48 -387
  26. package/tools/_connection-queue.js +45 -56
  27. package/tools/_discovery-enhanced.js +52 -57
  28. package/tools/_discovery.js +87 -185
  29. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  30. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  31. package/tools/_proactive-discovery.js +60 -73
  32. package/tools/_shared/index.js +41 -64
  33. package/tools/admin-inbox.js +10 -15
  34. package/tools/agents.js +1 -1
  35. package/tools/artifact-create.js +13 -23
  36. package/tools/artifact-view.js +4 -4
  37. package/tools/{_deprecated/back.js → back.js} +1 -1
  38. package/tools/bye.js +3 -5
  39. package/tools/consent.js +2 -2
  40. package/tools/context.js +9 -10
  41. package/tools/crossword.js +3 -2
  42. package/tools/discover.js +94 -356
  43. package/tools/dm.js +27 -86
  44. package/tools/doctor.js +12 -41
  45. package/tools/drawing.js +34 -20
  46. package/tools/echo.js +11 -11
  47. package/tools/feed.js +30 -58
  48. package/tools/follow.js +64 -187
  49. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  50. package/tools/game.js +144 -48
  51. package/tools/handoff.js +6 -8
  52. package/tools/help.js +3 -3
  53. package/tools/idea.js +15 -27
  54. package/tools/inbox.js +121 -293
  55. package/tools/init.js +54 -151
  56. package/tools/invite.js +8 -21
  57. package/tools/migrate.js +27 -24
  58. package/tools/multiplayer-game.js +50 -40
  59. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  60. package/tools/notifications.js +58 -48
  61. package/tools/observe.js +12 -15
  62. package/tools/onboarding.js +8 -11
  63. package/tools/open.js +13 -144
  64. package/tools/party-game.js +23 -12
  65. package/tools/patterns.js +2 -1
  66. package/tools/ping.js +5 -7
  67. package/tools/react.js +28 -30
  68. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  69. package/tools/release.js +4 -2
  70. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  71. package/tools/report.js +2 -2
  72. package/tools/request.js +6 -26
  73. package/tools/reserve.js +1 -1
  74. package/tools/session-fork.js +97 -0
  75. package/tools/session-save.js +109 -0
  76. package/tools/settings.js +30 -99
  77. package/tools/ship.js +74 -56
  78. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  79. package/tools/social-inbox.js +22 -28
  80. package/tools/social-post.js +24 -27
  81. package/tools/solo-game.js +54 -46
  82. package/tools/start.js +14 -148
  83. package/tools/status.js +21 -68
  84. package/tools/submit.js +4 -2
  85. package/tools/suggest-tags.js +36 -33
  86. package/tools/summarize.js +19 -16
  87. package/tools/tag-suggestions.js +72 -73
  88. package/tools/test.js +1 -1
  89. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  90. package/tools/token.js +4 -4
  91. package/tools/update.js +1 -2
  92. package/tools/watch.js +132 -112
  93. package/tools/who.js +20 -40
  94. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  95. package/tools/workshop-buddy.js +52 -53
  96. package/tools/x-mentions.js +0 -1
  97. package/tools/x-reply.js +0 -1
  98. package/twitter.js +14 -20
  99. package/version.json +8 -10
  100. package/analytics.js +0 -107
  101. package/auth-store.js +0 -148
  102. package/auto-update.js +0 -130
  103. package/bridges/bridge-monitor.js +0 -388
  104. package/bridges/discord-bot.js +0 -431
  105. package/bridges/farcaster.js +0 -299
  106. package/bridges/telegram.js +0 -261
  107. package/bridges/webhook-health.js +0 -420
  108. package/bridges/webhook-server.js +0 -437
  109. package/bridges/whatsapp.js +0 -441
  110. package/bridges/x-webhook.js +0 -423
  111. package/games/arcade.js +0 -406
  112. package/games/chess.js +0 -451
  113. package/games/colorguess.js +0 -343
  114. package/games/crossword-words.js +0 -171
  115. package/games/crossword.js +0 -461
  116. package/games/drawing.js +0 -347
  117. package/games/gameroulette.js +0 -300
  118. package/games/gamerouter.js +0 -336
  119. package/games/gamestatus.js +0 -337
  120. package/games/guessnumber.js +0 -209
  121. package/games/hangman.js +0 -279
  122. package/games/memory.js +0 -338
  123. package/games/multiplayer-tictactoe.js +0 -389
  124. package/games/pixelart.js +0 -399
  125. package/games/quickduel.js +0 -354
  126. package/games/riddle.js +0 -371
  127. package/games/rockpaperscissors.js +0 -291
  128. package/games/snake.js +0 -406
  129. package/games/storybuilder.js +0 -343
  130. package/games/tictactoe.js +0 -345
  131. package/games/twentyquestions.js +0 -286
  132. package/games/twotruths.js +0 -207
  133. package/games/werewolf.js +0 -508
  134. package/games/wordassociation.js +0 -247
  135. package/games/wordchain.js +0 -135
  136. package/intelligence/interests.js +0 -369
  137. package/notification-emitter.js +0 -77
  138. package/setup.js +0 -480
  139. package/smart-inbox.js +0 -276
  140. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  141. package/tools/_deprecated/bootstrap-skills.js +0 -231
  142. package/tools/_deprecated/bridge-dashboard.js +0 -342
  143. package/tools/_deprecated/bridge-health.js +0 -400
  144. package/tools/_deprecated/bridge-live.js +0 -384
  145. package/tools/_deprecated/bridges.js +0 -383
  146. package/tools/_deprecated/colorguess.js +0 -281
  147. package/tools/_deprecated/discover-insights.js +0 -379
  148. package/tools/_deprecated/discover-momentum.js +0 -256
  149. package/tools/_deprecated/discovery-analytics.js +0 -345
  150. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  151. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  152. package/tools/_deprecated/discovery-daily.js +0 -375
  153. package/tools/_deprecated/discovery-dashboard.js +0 -385
  154. package/tools/_deprecated/discovery-digest.js +0 -314
  155. package/tools/_deprecated/discovery-hub.js +0 -357
  156. package/tools/_deprecated/discovery-insights.js +0 -384
  157. package/tools/_deprecated/discovery-momentum.js +0 -281
  158. package/tools/_deprecated/discovery-monitor.js +0 -319
  159. package/tools/_deprecated/discovery-proactive.js +0 -300
  160. package/tools/_deprecated/draw.js +0 -317
  161. package/tools/_deprecated/farcaster.js +0 -307
  162. package/tools/_deprecated/games-catalog.js +0 -376
  163. package/tools/_deprecated/games.js +0 -313
  164. package/tools/_deprecated/guessnumber.js +0 -194
  165. package/tools/_deprecated/hangman.js +0 -129
  166. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  167. package/tools/_deprecated/riddle.js +0 -240
  168. package/tools/_deprecated/run-bootstrap.js +0 -69
  169. package/tools/_deprecated/skills-analytics.js +0 -349
  170. package/tools/_deprecated/skills-bootstrap.js +0 -301
  171. package/tools/_deprecated/skills-dashboard.js +0 -268
  172. package/tools/_deprecated/skills.js +0 -380
  173. package/tools/_deprecated/smart-intro.js +0 -353
  174. package/tools/_deprecated/storybuilder.js +0 -331
  175. package/tools/_deprecated/telegram-bot.js +0 -183
  176. package/tools/_deprecated/telegram-setup.js +0 -214
  177. package/tools/_deprecated/twentyquestions.js +0 -143
  178. package/tools/_shared.js +0 -234
  179. package/tools/_work-context.js +0 -338
  180. package/tools/_work-context.manual-test.js +0 -199
  181. package/tools/_work-context.test.js +0 -260
  182. package/tools/activity.js +0 -220
  183. package/tools/agent-treasury.js +0 -288
  184. package/tools/analytics.js +0 -191
  185. package/tools/approve.js +0 -197
  186. package/tools/arcade.js +0 -173
  187. package/tools/artifacts-price.js +0 -107
  188. package/tools/ask-expert.js +0 -160
  189. package/tools/available.js +0 -120
  190. package/tools/become-expert.js +0 -150
  191. package/tools/broadcast.js +0 -325
  192. package/tools/chat.js +0 -202
  193. package/tools/collaborative-drawing.js +0 -286
  194. package/tools/connection-status.js +0 -178
  195. package/tools/earnings.js +0 -126
  196. package/tools/friends.js +0 -207
  197. package/tools/genesis.js +0 -233
  198. package/tools/gig-browse.js +0 -206
  199. package/tools/gig-complete.js +0 -144
  200. package/tools/health.js +0 -87
  201. package/tools/leaderboard.js +0 -117
  202. package/tools/lib/git-apply.js +0 -206
  203. package/tools/lib/git-bundle.js +0 -407
  204. package/tools/mint.js +0 -377
  205. package/tools/plan.js +0 -225
  206. package/tools/profile.js +0 -219
  207. package/tools/proof-of-work.js +0 -144
  208. package/tools/pulse.js +0 -218
  209. package/tools/reply.js +0 -166
  210. package/tools/reputation.js +0 -175
  211. package/tools/schedule.js +0 -367
  212. package/tools/search-messages.js +0 -123
  213. package/tools/session.js +0 -467
  214. package/tools/session_price.js +0 -128
  215. package/tools/smart-check.js +0 -201
  216. package/tools/social-processor.js +0 -445
  217. package/tools/streak.js +0 -147
  218. package/tools/stuck.js +0 -297
  219. package/tools/subscribe.js +0 -148
  220. package/tools/subscriptions.js +0 -134
  221. package/tools/tip.js +0 -193
  222. package/tools/wallet.js +0 -269
  223. package/tools/webhook-test.js +0 -388
  224. package/tools/withdraw.js +0 -145
  225. package/tools/work-summary.js +0 -96
  226. package/tools/workshop.js +0 -327
  227. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  228. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  229. /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
- * Uses Universal Relevancy API for smart matching with social-first signals.
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
- let url = `${RELEVANCY_API}?handle=${encodeURIComponent(handle)}&context=discovery&limit=${limit}`;
71
- if (techTags) {
72
- url += `&tech_tags=${encodeURIComponent(techTags)}`;
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: 'Find interesting people to connect with, see who\'s live, browse upcoming sessions.',
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', 'live', 'upcoming', 'sessions', 'creators'],
124
- description: 'Discovery command: suggest, search, interests, active, live, upcoming, sessions, creators'
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 (local fallback)
257
- // This is used when API is unavailable
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) { // Minimum threshold
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 result = await getSuggestions(myHandle);
385
- const { matches, fromApi, tier } = result;
291
+ const suggestions = await getSuggestions(myHandle);
386
292
 
387
- if (!matches || matches.length === 0) {
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
- // Title varies by result tier
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 matches) {
417
- // Handle name with optional tier badge
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
- // Show reasons as a tree structure (from API)
423
- if (match.reasons && match.reasons.length > 0) {
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
- // Fallback for local matches (has interests array)
430
- else if (match.interests && match.interests.length > 0) {
314
+
315
+ if (match.interests.length > 0) {
431
316
  display += `💡 ${match.interests.slice(0, 3).join(', ')}\n`;
432
317
  }
433
318
 
434
- display += '\n';
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 { error: 'Please provide a search query: discover search "ai"' };
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.slice(0, 5).map(p => `@${p.handle}`).join(', ');
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}&section=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
- **Live & Sessions:**
684
- - \`discover live\`See who's broadcasting now
685
- - \`discover upcoming\` — Browse scheduled sessions
686
- - \`discover sessions\`Watch recent recorded sessions
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 };