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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/auto-update.js +10 -15
  4. package/config.js +36 -31
  5. package/crypto.js +1 -6
  6. package/debug.js +12 -0
  7. package/discord.js +19 -19
  8. package/eslint.config.js +54 -0
  9. package/index.js +217 -207
  10. package/intelligence/index.js +2 -9
  11. package/intelligence/infer.js +10 -16
  12. package/intelligence/patterns.js +23 -18
  13. package/intelligence/proactive.js +16 -15
  14. package/intelligence/serendipity.js +57 -20
  15. package/memory.js +13 -8
  16. package/migrate-v2.js +72 -0
  17. package/notification-emitter.js +2 -2
  18. package/notify.js +39 -14
  19. package/package.json +28 -29
  20. package/post-install.js +141 -0
  21. package/presence.js +2 -2
  22. package/prompts.js +5 -9
  23. package/protocol/index.js +123 -87
  24. package/protocol/telegram-commands.js +36 -37
  25. package/store/api.js +358 -529
  26. package/store/local.js +9 -10
  27. package/store/profiles.js +48 -192
  28. package/store/reservations.js +2 -9
  29. package/store/skills.js +69 -71
  30. package/store/sqlite.js +355 -0
  31. package/test-skills-bootstrap.js +20 -0
  32. package/test-v2-integration.js +385 -0
  33. package/tools/_actions.js +48 -387
  34. package/tools/_connection-queue.js +45 -56
  35. package/tools/_discovery-enhanced.js +52 -57
  36. package/tools/_discovery.js +87 -185
  37. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  38. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  39. package/tools/_proactive-discovery.js +60 -73
  40. package/tools/_shared/index.js +41 -64
  41. package/tools/admin-inbox.js +10 -15
  42. package/tools/agents.js +1 -1
  43. package/tools/artifact-create.js +13 -23
  44. package/tools/artifact-view.js +4 -4
  45. package/tools/{_deprecated/back.js → back.js} +1 -1
  46. package/tools/bye.js +3 -5
  47. package/tools/consent.js +2 -2
  48. package/tools/context.js +9 -10
  49. package/tools/crossword.js +3 -2
  50. package/tools/discover.js +94 -356
  51. package/tools/dm.js +27 -86
  52. package/tools/doctor.js +12 -41
  53. package/tools/drawing.js +34 -20
  54. package/tools/echo.js +11 -11
  55. package/tools/feed.js +30 -58
  56. package/tools/follow.js +64 -187
  57. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  58. package/tools/game.js +144 -48
  59. package/tools/handoff.js +6 -8
  60. package/tools/help.js +3 -3
  61. package/tools/idea.js +15 -27
  62. package/tools/inbox.js +121 -293
  63. package/tools/init.js +54 -151
  64. package/tools/invite.js +8 -21
  65. package/tools/migrate.js +27 -24
  66. package/tools/multiplayer-game.js +50 -40
  67. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  68. package/tools/notifications.js +58 -48
  69. package/tools/observe.js +12 -15
  70. package/tools/onboarding.js +8 -11
  71. package/tools/open.js +13 -144
  72. package/tools/party-game.js +23 -12
  73. package/tools/patterns.js +2 -1
  74. package/tools/ping.js +5 -7
  75. package/tools/react.js +28 -30
  76. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  77. package/tools/release.js +4 -2
  78. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  79. package/tools/report.js +2 -2
  80. package/tools/request.js +6 -26
  81. package/tools/reserve.js +1 -1
  82. package/tools/session-fork.js +97 -0
  83. package/tools/session-save.js +109 -0
  84. package/tools/settings.js +30 -99
  85. package/tools/ship.js +74 -56
  86. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  87. package/tools/social-inbox.js +22 -28
  88. package/tools/social-post.js +24 -27
  89. package/tools/solo-game.js +54 -46
  90. package/tools/start.js +14 -148
  91. package/tools/status.js +21 -68
  92. package/tools/submit.js +4 -2
  93. package/tools/suggest-tags.js +36 -33
  94. package/tools/summarize.js +19 -16
  95. package/tools/tag-suggestions.js +72 -73
  96. package/tools/test.js +1 -1
  97. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  98. package/tools/token.js +4 -4
  99. package/tools/update.js +1 -2
  100. package/tools/watch.js +132 -112
  101. package/tools/who.js +20 -40
  102. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  103. package/tools/workshop-buddy.js +52 -53
  104. package/tools/x-mentions.js +0 -1
  105. package/tools/x-reply.js +0 -1
  106. package/twitter.js +14 -20
  107. package/version.json +8 -10
  108. package/webhook-runner.js +132 -0
  109. package/auth-store.js +0 -148
  110. package/bridges/bridge-monitor.js +0 -388
  111. package/bridges/discord-bot.js +0 -431
  112. package/bridges/farcaster.js +0 -299
  113. package/bridges/telegram.js +0 -261
  114. package/bridges/webhook-health.js +0 -420
  115. package/bridges/webhook-server.js +0 -437
  116. package/bridges/whatsapp.js +0 -441
  117. package/bridges/x-webhook.js +0 -423
  118. package/games/arcade.js +0 -406
  119. package/games/chess.js +0 -451
  120. package/games/colorguess.js +0 -343
  121. package/games/crossword-words.js +0 -171
  122. package/games/crossword.js +0 -461
  123. package/games/drawing.js +0 -347
  124. package/games/gameroulette.js +0 -300
  125. package/games/gamerouter.js +0 -336
  126. package/games/gamestatus.js +0 -337
  127. package/games/guessnumber.js +0 -209
  128. package/games/hangman.js +0 -279
  129. package/games/memory.js +0 -338
  130. package/games/multiplayer-tictactoe.js +0 -389
  131. package/games/pixelart.js +0 -399
  132. package/games/quickduel.js +0 -354
  133. package/games/riddle.js +0 -371
  134. package/games/rockpaperscissors.js +0 -291
  135. package/games/snake.js +0 -406
  136. package/games/storybuilder.js +0 -343
  137. package/games/tictactoe.js +0 -345
  138. package/games/twentyquestions.js +0 -286
  139. package/games/twotruths.js +0 -207
  140. package/games/werewolf.js +0 -508
  141. package/games/wordassociation.js +0 -247
  142. package/games/wordchain.js +0 -135
  143. package/intelligence/interests.js +0 -369
  144. package/setup.js +0 -480
  145. package/smart-inbox.js +0 -276
  146. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  147. package/tools/_deprecated/bootstrap-skills.js +0 -231
  148. package/tools/_deprecated/bridge-dashboard.js +0 -342
  149. package/tools/_deprecated/bridge-health.js +0 -400
  150. package/tools/_deprecated/bridge-live.js +0 -384
  151. package/tools/_deprecated/bridges.js +0 -383
  152. package/tools/_deprecated/colorguess.js +0 -281
  153. package/tools/_deprecated/discover-insights.js +0 -379
  154. package/tools/_deprecated/discover-momentum.js +0 -256
  155. package/tools/_deprecated/discovery-analytics.js +0 -345
  156. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  157. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  158. package/tools/_deprecated/discovery-daily.js +0 -375
  159. package/tools/_deprecated/discovery-dashboard.js +0 -385
  160. package/tools/_deprecated/discovery-digest.js +0 -314
  161. package/tools/_deprecated/discovery-hub.js +0 -357
  162. package/tools/_deprecated/discovery-insights.js +0 -384
  163. package/tools/_deprecated/discovery-momentum.js +0 -281
  164. package/tools/_deprecated/discovery-monitor.js +0 -319
  165. package/tools/_deprecated/discovery-proactive.js +0 -300
  166. package/tools/_deprecated/draw.js +0 -317
  167. package/tools/_deprecated/farcaster.js +0 -307
  168. package/tools/_deprecated/games-catalog.js +0 -376
  169. package/tools/_deprecated/games.js +0 -313
  170. package/tools/_deprecated/guessnumber.js +0 -194
  171. package/tools/_deprecated/hangman.js +0 -129
  172. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  173. package/tools/_deprecated/riddle.js +0 -240
  174. package/tools/_deprecated/run-bootstrap.js +0 -69
  175. package/tools/_deprecated/skills-analytics.js +0 -349
  176. package/tools/_deprecated/skills-bootstrap.js +0 -301
  177. package/tools/_deprecated/skills-dashboard.js +0 -268
  178. package/tools/_deprecated/skills.js +0 -380
  179. package/tools/_deprecated/smart-intro.js +0 -353
  180. package/tools/_deprecated/storybuilder.js +0 -331
  181. package/tools/_deprecated/telegram-bot.js +0 -183
  182. package/tools/_deprecated/telegram-setup.js +0 -214
  183. package/tools/_deprecated/twentyquestions.js +0 -143
  184. package/tools/_shared.js +0 -234
  185. package/tools/_work-context.js +0 -338
  186. package/tools/_work-context.manual-test.js +0 -199
  187. package/tools/_work-context.test.js +0 -260
  188. package/tools/activity.js +0 -220
  189. package/tools/agent-treasury.js +0 -288
  190. package/tools/analytics.js +0 -191
  191. package/tools/approve.js +0 -197
  192. package/tools/arcade.js +0 -173
  193. package/tools/artifacts-price.js +0 -107
  194. package/tools/ask-expert.js +0 -160
  195. package/tools/available.js +0 -120
  196. package/tools/become-expert.js +0 -150
  197. package/tools/broadcast.js +0 -325
  198. package/tools/chat.js +0 -202
  199. package/tools/collaborative-drawing.js +0 -286
  200. package/tools/connection-status.js +0 -178
  201. package/tools/earnings.js +0 -126
  202. package/tools/friends.js +0 -207
  203. package/tools/genesis.js +0 -233
  204. package/tools/gig-browse.js +0 -206
  205. package/tools/gig-complete.js +0 -144
  206. package/tools/health.js +0 -87
  207. package/tools/leaderboard.js +0 -117
  208. package/tools/lib/git-apply.js +0 -206
  209. package/tools/lib/git-bundle.js +0 -407
  210. package/tools/mint.js +0 -377
  211. package/tools/plan.js +0 -225
  212. package/tools/profile.js +0 -219
  213. package/tools/proof-of-work.js +0 -144
  214. package/tools/pulse.js +0 -218
  215. package/tools/reply.js +0 -166
  216. package/tools/reputation.js +0 -175
  217. package/tools/schedule.js +0 -367
  218. package/tools/search-messages.js +0 -123
  219. package/tools/session.js +0 -467
  220. package/tools/session_price.js +0 -128
  221. package/tools/smart-check.js +0 -201
  222. package/tools/social-processor.js +0 -445
  223. package/tools/streak.js +0 -147
  224. package/tools/stuck.js +0 -297
  225. package/tools/subscribe.js +0 -148
  226. package/tools/subscriptions.js +0 -134
  227. package/tools/tip.js +0 -193
  228. package/tools/wallet.js +0 -269
  229. package/tools/webhook-test.js +0 -388
  230. package/tools/withdraw.js +0 -145
  231. package/tools/work-summary.js +0 -96
  232. package/tools/workshop.js +0 -327
  233. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  234. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  235. /package/tools/{_deprecated/away.js → away.js} +0 -0
package/smart-inbox.js DELETED
@@ -1,276 +0,0 @@
1
- /**
2
- * Smart Inbox Checking
3
- *
4
- * Intelligent notification checking that triggers at natural breaks:
5
- * - After git commits
6
- * - After test/build completion
7
- * - After periods of activity (not idle)
8
- * - On explicit check request
9
- *
10
- * Uses debouncing to avoid notification spam.
11
- */
12
-
13
- const config = require('./config');
14
- const notify = require('./notify');
15
- const store = require('./store');
16
- const fs = require('fs');
17
- const path = require('path');
18
-
19
- // State file for tracking check timing
20
- const SMART_STATE_FILE = path.join(config.VIBE_DIR, '.smart_inbox_state.json');
21
-
22
- // Timing constants
23
- const MIN_CHECK_INTERVAL = 60 * 1000; // Minimum 1 minute between checks
24
- const ACTIVITY_COOLDOWN = 5 * 60 * 1000; // 5 min after activity burst
25
- const COMMIT_DELAY = 3 * 1000; // 3 second delay after commit (let it settle)
26
- const TEST_DELAY = 2 * 1000; // 2 second delay after test
27
- const BUILD_DELAY = 5 * 1000; // 5 second delay after build
28
-
29
- // Trigger types
30
- const TRIGGERS = {
31
- COMMIT: 'commit',
32
- TEST_PASS: 'test_pass',
33
- TEST_FAIL: 'test_fail',
34
- BUILD_COMPLETE: 'build_complete',
35
- BUILD_FAIL: 'build_fail',
36
- NATURAL_BREAK: 'natural_break',
37
- EXPLICIT: 'explicit',
38
- SESSION_START: 'session_start',
39
- SESSION_END: 'session_end',
40
- };
41
-
42
- function loadState() {
43
- try {
44
- if (fs.existsSync(SMART_STATE_FILE)) {
45
- return JSON.parse(fs.readFileSync(SMART_STATE_FILE, 'utf8'));
46
- }
47
- } catch (e) {}
48
- return {
49
- lastCheck: null,
50
- lastTrigger: null,
51
- checkCount: 0,
52
- triggersToday: {},
53
- activityBurst: null,
54
- };
55
- }
56
-
57
- function saveState(state) {
58
- try {
59
- fs.writeFileSync(SMART_STATE_FILE, JSON.stringify(state, null, 2));
60
- } catch (e) {}
61
- }
62
-
63
- /**
64
- * Check if we should actually run a check based on timing
65
- */
66
- function shouldCheck(trigger) {
67
- const state = loadState();
68
- const now = Date.now();
69
-
70
- // Always allow explicit checks
71
- if (trigger === TRIGGERS.EXPLICIT) {
72
- return { should: true, reason: 'explicit_request' };
73
- }
74
-
75
- // Check minimum interval
76
- if (state.lastCheck && (now - state.lastCheck) < MIN_CHECK_INTERVAL) {
77
- return { should: false, reason: 'too_soon', wait: MIN_CHECK_INTERVAL - (now - state.lastCheck) };
78
- }
79
-
80
- // Session events always trigger
81
- if (trigger === TRIGGERS.SESSION_START || trigger === TRIGGERS.SESSION_END) {
82
- return { should: true, reason: 'session_event' };
83
- }
84
-
85
- // For build/test/commit, we want them but with debounce
86
- if ([TRIGGERS.COMMIT, TRIGGERS.TEST_PASS, TRIGGERS.TEST_FAIL,
87
- TRIGGERS.BUILD_COMPLETE, TRIGGERS.BUILD_FAIL].includes(trigger)) {
88
- // If same trigger happened in last 30 seconds, skip
89
- if (state.lastTrigger === trigger && state.lastCheck && (now - state.lastCheck) < 30000) {
90
- return { should: false, reason: 'debounced', wait: 30000 - (now - state.lastCheck) };
91
- }
92
- return { should: true, reason: 'event_trigger' };
93
- }
94
-
95
- // Natural breaks - check if there's been activity recently
96
- if (trigger === TRIGGERS.NATURAL_BREAK) {
97
- // If we haven't checked in 5+ minutes and there's been activity, good time
98
- if (!state.lastCheck || (now - state.lastCheck) > ACTIVITY_COOLDOWN) {
99
- return { should: true, reason: 'natural_break' };
100
- }
101
- return { should: false, reason: 'not_time_yet' };
102
- }
103
-
104
- return { should: true, reason: 'default' };
105
- }
106
-
107
- /**
108
- * Perform smart inbox check
109
- * Returns notification info if any
110
- */
111
- async function check(trigger = TRIGGERS.EXPLICIT, context = {}) {
112
- const decision = shouldCheck(trigger);
113
-
114
- if (!decision.should) {
115
- return {
116
- checked: false,
117
- reason: decision.reason,
118
- waitMs: decision.wait || 0,
119
- };
120
- }
121
-
122
- const handle = config.getHandle();
123
- if (!handle) {
124
- return { checked: false, reason: 'not_initialized' };
125
- }
126
-
127
- const state = loadState();
128
- const now = Date.now();
129
-
130
- // Apply appropriate delay based on trigger
131
- let delay = 0;
132
- switch (trigger) {
133
- case TRIGGERS.COMMIT:
134
- delay = COMMIT_DELAY;
135
- break;
136
- case TRIGGERS.TEST_PASS:
137
- case TRIGGERS.TEST_FAIL:
138
- delay = TEST_DELAY;
139
- break;
140
- case TRIGGERS.BUILD_COMPLETE:
141
- case TRIGGERS.BUILD_FAIL:
142
- delay = BUILD_DELAY;
143
- break;
144
- }
145
-
146
- if (delay > 0) {
147
- await new Promise(r => setTimeout(r, delay));
148
- }
149
-
150
- // Run the actual notification check
151
- let unreadCount = 0;
152
- let notified = false;
153
- let newUsers = [];
154
- let newShips = [];
155
-
156
- try {
157
- // Get unread messages
158
- const inbox = await store.getRawInbox(handle).catch(() => []);
159
- unreadCount = inbox.length;
160
-
161
- if (inbox.length > 0) {
162
- notified = await notify.checkAndNotify(inbox);
163
- }
164
-
165
- // Get presence for online users
166
- const users = await store.getActiveUsers().catch(() => []);
167
- if (users.length > 0) {
168
- newUsers = notify.checkPresence(users);
169
- }
170
-
171
- // Get ships from connections
172
- try {
173
- const localStore = require('./store/local');
174
- const memories = localStore.getAllThreadMemories ? localStore.getAllThreadMemories() : {};
175
- const memoryHandles = Object.keys(memories).map(h => h.toLowerCase());
176
- if (memoryHandles.length > 0) {
177
- newShips = await notify.checkShips(memoryHandles);
178
- }
179
- } catch (e) {
180
- // Memory check is optional
181
- }
182
- } catch (e) {
183
- // Silent fail
184
- }
185
-
186
- // Update state
187
- state.lastCheck = now;
188
- state.lastTrigger = trigger;
189
- state.checkCount++;
190
-
191
- // Track triggers by day
192
- const today = new Date().toISOString().split('T')[0];
193
- if (!state.triggersToday || state.triggersToday.date !== today) {
194
- state.triggersToday = { date: today, counts: {} };
195
- }
196
- state.triggersToday.counts[trigger] = (state.triggersToday.counts[trigger] || 0) + 1;
197
-
198
- saveState(state);
199
-
200
- return {
201
- checked: true,
202
- trigger,
203
- reason: decision.reason,
204
- unreadCount,
205
- notified,
206
- newOnline: newUsers.map(u => u.handle),
207
- newShips: newShips.map(s => s.author),
208
- context,
209
- timestamp: new Date().toISOString(),
210
- };
211
- }
212
-
213
- /**
214
- * Trigger after git commit
215
- */
216
- async function afterCommit(commitMessage = '', branch = '') {
217
- return check(TRIGGERS.COMMIT, { commitMessage, branch });
218
- }
219
-
220
- /**
221
- * Trigger after test completion
222
- */
223
- async function afterTest(passed = true, testOutput = '') {
224
- const trigger = passed ? TRIGGERS.TEST_PASS : TRIGGERS.TEST_FAIL;
225
- return check(trigger, { passed, testOutput: testOutput.slice(0, 200) });
226
- }
227
-
228
- /**
229
- * Trigger after build completion
230
- */
231
- async function afterBuild(success = true, buildOutput = '') {
232
- const trigger = success ? TRIGGERS.BUILD_COMPLETE : TRIGGERS.BUILD_FAIL;
233
- return check(trigger, { success, buildOutput: buildOutput.slice(0, 200) });
234
- }
235
-
236
- /**
237
- * Trigger on natural break (between tasks, file saves, etc.)
238
- */
239
- async function onNaturalBreak(activity = '') {
240
- return check(TRIGGERS.NATURAL_BREAK, { activity });
241
- }
242
-
243
- /**
244
- * Explicit check (user requested)
245
- */
246
- async function checkNow() {
247
- return check(TRIGGERS.EXPLICIT);
248
- }
249
-
250
- /**
251
- * Get check stats
252
- */
253
- function getStats() {
254
- const state = loadState();
255
- const now = Date.now();
256
-
257
- return {
258
- lastCheck: state.lastCheck ? new Date(state.lastCheck).toISOString() : null,
259
- lastTrigger: state.lastTrigger,
260
- checkCount: state.checkCount,
261
- todayTriggers: state.triggersToday?.counts || {},
262
- timeSinceLastCheck: state.lastCheck ? Math.floor((now - state.lastCheck) / 1000) : null,
263
- };
264
- }
265
-
266
- module.exports = {
267
- TRIGGERS,
268
- check,
269
- afterCommit,
270
- afterTest,
271
- afterBuild,
272
- onNaturalBreak,
273
- checkNow,
274
- getStats,
275
- shouldCheck,
276
- };
@@ -1,304 +0,0 @@
1
- /**
2
- * vibe auto-suggest-connections — Smart connection recommendations
3
- *
4
- * Analyzes skills exchanges and user profiles to automatically suggest
5
- * connections between people with complementary skills or interests.
6
- */
7
-
8
- const store = require('../store');
9
- const userProfiles = require('../store/profiles');
10
- const { formatTimeAgo, requireInit } = require('./_shared');
11
-
12
- const definition = {
13
- name: 'vibe_auto_suggest_connections',
14
- description: 'Find and suggest connections based on complementary skills and interests.',
15
- inputSchema: {
16
- type: 'object',
17
- properties: {
18
- mode: {
19
- type: 'string',
20
- enum: ['skills', 'building', 'interests', 'all'],
21
- description: 'Type of connection analysis to run',
22
- default: 'skills'
23
- },
24
- limit: {
25
- type: 'number',
26
- description: 'Maximum number of suggestions to generate',
27
- default: 5
28
- },
29
- execute: {
30
- type: 'boolean',
31
- description: 'Actually send suggestions to users (vs just preview)',
32
- default: false
33
- }
34
- }
35
- }
36
- };
37
-
38
- // Find skill-based connection opportunities
39
- async function findSkillConnections() {
40
- const posts = await store.getSkillExchanges() || [];
41
- const connections = [];
42
-
43
- // Build skill map
44
- const skillMap = {};
45
- posts.forEach(post => {
46
- if (!skillMap[post.skill]) {
47
- skillMap[post.skill] = { offers: [], requests: [] };
48
- }
49
- skillMap[post.skill][post.type + 's'].push(post);
50
- });
51
-
52
- // Find perfect matches
53
- Object.entries(skillMap).forEach(([skill, data]) => {
54
- data.offers.forEach(offer => {
55
- data.requests.forEach(request => {
56
- if (offer.handle !== request.handle) {
57
- connections.push({
58
- type: 'skill_exchange',
59
- skill,
60
- expert: offer.handle,
61
- learner: request.handle,
62
- reason: `@${offer.handle} offers "${skill}" expertise and @${request.handle} is looking to learn it`,
63
- confidence: 95,
64
- details: {
65
- offerDetails: offer.details,
66
- requestDetails: request.details,
67
- category: offer.category
68
- }
69
- });
70
- }
71
- });
72
- });
73
- });
74
-
75
- return connections;
76
- }
77
-
78
- // Find building/project-based connections
79
- async function findBuildingConnections() {
80
- const profiles = await userProfiles.getAllProfiles();
81
- const connections = [];
82
-
83
- const users = Object.values(profiles).filter(p => p.building);
84
-
85
- for (let i = 0; i < users.length; i++) {
86
- for (let j = i + 1; j < users.length; j++) {
87
- const user1 = users[i];
88
- const user2 = users[j];
89
-
90
- const building1 = user1.building.toLowerCase();
91
- const building2 = user2.building.toLowerCase();
92
-
93
- // Check for similar projects or complementary skills
94
- const commonWords = building1.split(' ').filter(word =>
95
- word.length > 3 && building2.includes(word)
96
- );
97
-
98
- if (commonWords.length > 0) {
99
- connections.push({
100
- type: 'similar_building',
101
- person1: user1.handle,
102
- person2: user2.handle,
103
- reason: `Both building similar projects: "${user1.building}" and "${user2.building}"`,
104
- confidence: 75,
105
- details: {
106
- commonWords,
107
- project1: user1.building,
108
- project2: user2.building
109
- }
110
- });
111
- }
112
- }
113
- }
114
-
115
- return connections;
116
- }
117
-
118
- // Find interest-based connections
119
- async function findInterestConnections() {
120
- const profiles = await userProfiles.getAllProfiles();
121
- const connections = [];
122
-
123
- const users = Object.values(profiles).filter(p => p.interests && p.interests.length > 0);
124
-
125
- for (let i = 0; i < users.length; i++) {
126
- for (let j = i + 1; j < users.length; j++) {
127
- const user1 = users[i];
128
- const user2 = users[j];
129
-
130
- const commonInterests = user1.interests.filter(interest =>
131
- user2.interests.some(i2 =>
132
- i2.toLowerCase() === interest.toLowerCase() ||
133
- i2.toLowerCase().includes(interest.toLowerCase()) ||
134
- interest.toLowerCase().includes(i2.toLowerCase())
135
- )
136
- );
137
-
138
- if (commonInterests.length > 0) {
139
- connections.push({
140
- type: 'shared_interests',
141
- person1: user1.handle,
142
- person2: user2.handle,
143
- reason: `Share interests in: ${commonInterests.join(', ')}`,
144
- confidence: 60 + (commonInterests.length * 10),
145
- details: {
146
- commonInterests,
147
- user1Interests: user1.interests,
148
- user2Interests: user2.interests
149
- }
150
- });
151
- }
152
- }
153
- }
154
-
155
- return connections;
156
- }
157
-
158
- // Format connection suggestion
159
- function formatSuggestion(connection) {
160
- switch (connection.type) {
161
- case 'skill_exchange':
162
- return {
163
- from: connection.learner,
164
- to: connection.expert,
165
- reason: `You both are interested in "${connection.skill}"! @${connection.expert} offers expertise and you're looking to learn. Great opportunity to connect!`,
166
- altReason: `@${connection.learner} is looking to learn "${connection.skill}" which you offer expertise in. Consider reaching out to help!`
167
- };
168
-
169
- case 'similar_building':
170
- return {
171
- from: connection.person1,
172
- to: connection.person2,
173
- reason: `You might want to connect with @${connection.person2} — you're both building similar projects! "${connection.details.project1}" and "${connection.details.project2}"`
174
- };
175
-
176
- case 'shared_interests':
177
- return {
178
- from: connection.person1,
179
- to: connection.person2,
180
- reason: `You share interests with @${connection.person2} in: ${connection.details.commonInterests.join(', ')}. Could be a great collaboration!`
181
- };
182
-
183
- default:
184
- return {
185
- from: connection.person1 || connection.expert,
186
- to: connection.person2 || connection.learner,
187
- reason: connection.reason
188
- };
189
- }
190
- }
191
-
192
- async function handler(args) {
193
- const initCheck = requireInit();
194
- if (initCheck) return initCheck;
195
-
196
- const mode = args.mode || 'skills';
197
- const limit = Math.min(args.limit || 5, 10);
198
- const execute = args.execute === true;
199
-
200
- let display = '';
201
-
202
- try {
203
- display = `## Auto-Suggest Connections 🤖\n\n`;
204
-
205
- let allConnections = [];
206
-
207
- // Gather connections based on mode
208
- if (mode === 'skills' || mode === 'all') {
209
- const skillConnections = await findSkillConnections();
210
- allConnections = allConnections.concat(skillConnections);
211
- }
212
-
213
- if (mode === 'building' || mode === 'all') {
214
- const buildingConnections = await findBuildingConnections();
215
- allConnections = allConnections.concat(buildingConnections);
216
- }
217
-
218
- if (mode === 'interests' || mode === 'all') {
219
- const interestConnections = await findInterestConnections();
220
- allConnections = allConnections.concat(interestConnections);
221
- }
222
-
223
- // Sort by confidence and limit
224
- allConnections.sort((a, b) => b.confidence - a.confidence);
225
- const topConnections = allConnections.slice(0, limit);
226
-
227
- if (topConnections.length === 0) {
228
- display += `**No connection opportunities found** in "${mode}" mode.\n\n`;
229
- display += `**Try:**\n`;
230
- display += `• \`auto-suggest-connections --mode all\` — Check all types\n`;
231
- display += `• \`bootstrap-skills\` — Create sample skill exchanges\n`;
232
- display += `• Users can \`skills-exchange post\` to create opportunities\n`;
233
- return { display };
234
- }
235
-
236
- display += `**Found ${topConnections.length} connection opportunities** (mode: ${mode})\n\n`;
237
-
238
- let suggestions = [];
239
-
240
- for (const connection of topConnections) {
241
- const formatted = formatSuggestion(connection);
242
- suggestions.push(formatted);
243
-
244
- display += `### ${connection.type.replace('_', ' ').toUpperCase()} (${connection.confidence}% match)\n`;
245
- display += `**Connection:** @${formatted.from} ↔ @${formatted.to}\n`;
246
- display += `**Reason:** ${formatted.reason}\n`;
247
-
248
- if (connection.details) {
249
- display += `**Context:** `;
250
- if (connection.type === 'skill_exchange') {
251
- display += `Expert: "${connection.details.offerDetails || 'available'}" / Learner: "${connection.details.requestDetails || 'seeking help'}"`;
252
- } else if (connection.type === 'similar_building') {
253
- display += `"${connection.details.project1}" + "${connection.details.project2}"`;
254
- } else if (connection.type === 'shared_interests') {
255
- display += `${connection.details.commonInterests.join(', ')}`;
256
- }
257
- display += `\n`;
258
- }
259
-
260
- if (execute) {
261
- display += `**✅ SUGGESTED** — DM sent to @${formatted.from}\n`;
262
- } else {
263
- display += `**Preview** — Would suggest to @${formatted.from}\n`;
264
- }
265
-
266
- display += `\n`;
267
- }
268
-
269
- if (execute) {
270
- // Actually send the suggestions
271
- let sent = 0;
272
- for (const suggestion of suggestions) {
273
- try {
274
- await store.sendMessage(
275
- 'discovery-agent',
276
- suggestion.from,
277
- `🤝 Connection Suggestion: ${suggestion.reason}`,
278
- 'suggestion'
279
- );
280
- sent++;
281
- } catch (error) {
282
- console.error(`Failed to send suggestion to @${suggestion.from}:`, error);
283
- }
284
- }
285
-
286
- display += `**✅ Results: ${sent}/${suggestions.length} suggestions sent successfully**\n\n`;
287
- } else {
288
- display += `**💡 To actually send these suggestions:**\n`;
289
- display += `\`auto-suggest-connections --mode ${mode} --execute true\`\n\n`;
290
- }
291
-
292
- display += `**Connection Dashboard:**\n`;
293
- display += `• Skills exchanges: Use \`skills-exchange browse\`\n`;
294
- display += `• User profiles: Use \`who\` to see who's building what\n`;
295
- display += `• Direct connections: \`dm @username\` to connect directly\n`;
296
-
297
- } catch (error) {
298
- display = `## Auto-Suggest Error\n\n${error.message}\n\nTry: \`auto-suggest-connections\` for basic suggestions`;
299
- }
300
-
301
- return { display };
302
- }
303
-
304
- module.exports = { definition, handler };