slashvibe-mcp 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +280 -47
- package/auto-update.js +10 -15
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/debug.js +12 -0
- package/discord.js +19 -19
- package/eslint.config.js +54 -0
- package/index.js +217 -207
- package/intelligence/index.js +2 -9
- package/intelligence/infer.js +10 -16
- package/intelligence/patterns.js +23 -18
- package/intelligence/proactive.js +16 -15
- package/intelligence/serendipity.js +57 -20
- package/memory.js +13 -8
- package/migrate-v2.js +72 -0
- package/notification-emitter.js +2 -2
- package/notify.js +39 -14
- package/package.json +28 -29
- package/post-install.js +141 -0
- package/presence.js +2 -2
- package/prompts.js +5 -9
- package/protocol/index.js +123 -87
- package/protocol/telegram-commands.js +36 -37
- package/store/api.js +358 -529
- package/store/local.js +9 -10
- package/store/profiles.js +48 -192
- package/store/reservations.js +2 -9
- package/store/skills.js +69 -71
- package/store/sqlite.js +355 -0
- package/test-skills-bootstrap.js +20 -0
- package/test-v2-integration.js +385 -0
- package/tools/_actions.js +48 -387
- package/tools/_connection-queue.js +45 -56
- package/tools/_discovery-enhanced.js +52 -57
- package/tools/_discovery.js +87 -185
- package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
- package/tools/{shipback.js → _experimental/shipback.js} +4 -3
- package/tools/_proactive-discovery.js +60 -73
- package/tools/_shared/index.js +41 -64
- package/tools/admin-inbox.js +10 -15
- package/tools/agents.js +1 -1
- package/tools/artifact-create.js +13 -23
- package/tools/artifact-view.js +4 -4
- package/tools/{_deprecated/back.js → back.js} +1 -1
- package/tools/bye.js +3 -5
- package/tools/consent.js +2 -2
- package/tools/context.js +9 -10
- package/tools/crossword.js +3 -2
- package/tools/discover.js +94 -356
- package/tools/dm.js +27 -86
- package/tools/doctor.js +12 -41
- package/tools/drawing.js +34 -20
- package/tools/echo.js +11 -11
- package/tools/feed.js +30 -58
- package/tools/follow.js +64 -187
- package/tools/{_deprecated/forget.js → forget.js} +4 -7
- package/tools/game.js +144 -48
- package/tools/handoff.js +6 -8
- package/tools/help.js +3 -3
- package/tools/idea.js +15 -27
- package/tools/inbox.js +121 -293
- package/tools/init.js +54 -151
- package/tools/invite.js +8 -21
- package/tools/migrate.js +27 -24
- package/tools/multiplayer-game.js +50 -40
- package/tools/{_deprecated/mute.js → mute.js} +4 -3
- package/tools/notifications.js +58 -48
- package/tools/observe.js +12 -15
- package/tools/onboarding.js +8 -11
- package/tools/open.js +13 -144
- package/tools/party-game.js +23 -12
- package/tools/patterns.js +2 -1
- package/tools/ping.js +5 -7
- package/tools/react.js +28 -30
- package/tools/{_deprecated/recall.js → recall.js} +5 -10
- package/tools/release.js +4 -2
- package/tools/{_deprecated/remember.js → remember.js} +4 -6
- package/tools/report.js +2 -2
- package/tools/request.js +6 -26
- package/tools/reserve.js +1 -1
- package/tools/session-fork.js +97 -0
- package/tools/session-save.js +109 -0
- package/tools/settings.js +30 -99
- package/tools/ship.js +74 -56
- package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
- package/tools/social-inbox.js +22 -28
- package/tools/social-post.js +24 -27
- package/tools/solo-game.js +54 -46
- package/tools/start.js +14 -148
- package/tools/status.js +21 -68
- package/tools/submit.js +4 -2
- package/tools/suggest-tags.js +36 -33
- package/tools/summarize.js +19 -16
- package/tools/tag-suggestions.js +72 -73
- package/tools/test.js +1 -1
- package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
- package/tools/token.js +4 -4
- package/tools/update.js +1 -2
- package/tools/watch.js +132 -112
- package/tools/who.js +20 -40
- package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
- package/tools/workshop-buddy.js +52 -53
- package/tools/x-mentions.js +0 -1
- package/tools/x-reply.js +0 -1
- package/twitter.js +14 -20
- package/version.json +8 -10
- package/webhook-runner.js +132 -0
- package/auth-store.js +0 -148
- package/bridges/bridge-monitor.js +0 -388
- package/bridges/discord-bot.js +0 -431
- package/bridges/farcaster.js +0 -299
- package/bridges/telegram.js +0 -261
- package/bridges/webhook-health.js +0 -420
- package/bridges/webhook-server.js +0 -437
- package/bridges/whatsapp.js +0 -441
- package/bridges/x-webhook.js +0 -423
- package/games/arcade.js +0 -406
- package/games/chess.js +0 -451
- package/games/colorguess.js +0 -343
- package/games/crossword-words.js +0 -171
- package/games/crossword.js +0 -461
- package/games/drawing.js +0 -347
- package/games/gameroulette.js +0 -300
- package/games/gamerouter.js +0 -336
- package/games/gamestatus.js +0 -337
- package/games/guessnumber.js +0 -209
- package/games/hangman.js +0 -279
- package/games/memory.js +0 -338
- package/games/multiplayer-tictactoe.js +0 -389
- package/games/pixelart.js +0 -399
- package/games/quickduel.js +0 -354
- package/games/riddle.js +0 -371
- package/games/rockpaperscissors.js +0 -291
- package/games/snake.js +0 -406
- package/games/storybuilder.js +0 -343
- package/games/tictactoe.js +0 -345
- package/games/twentyquestions.js +0 -286
- package/games/twotruths.js +0 -207
- package/games/werewolf.js +0 -508
- package/games/wordassociation.js +0 -247
- package/games/wordchain.js +0 -135
- package/intelligence/interests.js +0 -369
- package/setup.js +0 -480
- package/smart-inbox.js +0 -276
- package/tools/_deprecated/auto-suggest-connections.js +0 -304
- package/tools/_deprecated/bootstrap-skills.js +0 -231
- package/tools/_deprecated/bridge-dashboard.js +0 -342
- package/tools/_deprecated/bridge-health.js +0 -400
- package/tools/_deprecated/bridge-live.js +0 -384
- package/tools/_deprecated/bridges.js +0 -383
- package/tools/_deprecated/colorguess.js +0 -281
- package/tools/_deprecated/discover-insights.js +0 -379
- package/tools/_deprecated/discover-momentum.js +0 -256
- package/tools/_deprecated/discovery-analytics.js +0 -345
- package/tools/_deprecated/discovery-auto-suggest.js +0 -275
- package/tools/_deprecated/discovery-bootstrap.js +0 -267
- package/tools/_deprecated/discovery-daily.js +0 -375
- package/tools/_deprecated/discovery-dashboard.js +0 -385
- package/tools/_deprecated/discovery-digest.js +0 -314
- package/tools/_deprecated/discovery-hub.js +0 -357
- package/tools/_deprecated/discovery-insights.js +0 -384
- package/tools/_deprecated/discovery-momentum.js +0 -281
- package/tools/_deprecated/discovery-monitor.js +0 -319
- package/tools/_deprecated/discovery-proactive.js +0 -300
- package/tools/_deprecated/draw.js +0 -317
- package/tools/_deprecated/farcaster.js +0 -307
- package/tools/_deprecated/games-catalog.js +0 -376
- package/tools/_deprecated/games.js +0 -313
- package/tools/_deprecated/guessnumber.js +0 -194
- package/tools/_deprecated/hangman.js +0 -129
- package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
- package/tools/_deprecated/riddle.js +0 -240
- package/tools/_deprecated/run-bootstrap.js +0 -69
- package/tools/_deprecated/skills-analytics.js +0 -349
- package/tools/_deprecated/skills-bootstrap.js +0 -301
- package/tools/_deprecated/skills-dashboard.js +0 -268
- package/tools/_deprecated/skills.js +0 -380
- package/tools/_deprecated/smart-intro.js +0 -353
- package/tools/_deprecated/storybuilder.js +0 -331
- package/tools/_deprecated/telegram-bot.js +0 -183
- package/tools/_deprecated/telegram-setup.js +0 -214
- package/tools/_deprecated/twentyquestions.js +0 -143
- package/tools/_shared.js +0 -234
- package/tools/_work-context.js +0 -338
- package/tools/_work-context.manual-test.js +0 -199
- package/tools/_work-context.test.js +0 -260
- package/tools/activity.js +0 -220
- package/tools/agent-treasury.js +0 -288
- package/tools/analytics.js +0 -191
- package/tools/approve.js +0 -197
- package/tools/arcade.js +0 -173
- package/tools/artifacts-price.js +0 -107
- package/tools/ask-expert.js +0 -160
- package/tools/available.js +0 -120
- package/tools/become-expert.js +0 -150
- package/tools/broadcast.js +0 -325
- package/tools/chat.js +0 -202
- package/tools/collaborative-drawing.js +0 -286
- package/tools/connection-status.js +0 -178
- package/tools/earnings.js +0 -126
- package/tools/friends.js +0 -207
- package/tools/genesis.js +0 -233
- package/tools/gig-browse.js +0 -206
- package/tools/gig-complete.js +0 -144
- package/tools/health.js +0 -87
- package/tools/leaderboard.js +0 -117
- package/tools/lib/git-apply.js +0 -206
- package/tools/lib/git-bundle.js +0 -407
- package/tools/mint.js +0 -377
- package/tools/plan.js +0 -225
- package/tools/profile.js +0 -219
- package/tools/proof-of-work.js +0 -144
- package/tools/pulse.js +0 -218
- package/tools/reply.js +0 -166
- package/tools/reputation.js +0 -175
- package/tools/schedule.js +0 -367
- package/tools/search-messages.js +0 -123
- package/tools/session.js +0 -467
- package/tools/session_price.js +0 -128
- package/tools/smart-check.js +0 -201
- package/tools/social-processor.js +0 -445
- package/tools/streak.js +0 -147
- package/tools/stuck.js +0 -297
- package/tools/subscribe.js +0 -148
- package/tools/subscriptions.js +0 -134
- package/tools/tip.js +0 -193
- package/tools/wallet.js +0 -269
- package/tools/webhook-test.js +0 -388
- package/tools/withdraw.js +0 -145
- package/tools/work-summary.js +0 -96
- package/tools/workshop.js +0 -327
- /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
- /package/tools/{l2.js → _experimental/l2.js} +0 -0
- /package/tools/{_deprecated/away.js → away.js} +0 -0
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 };
|