slashvibe-mcp 0.3.21 → 0.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +280 -47
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/discord.js +19 -19
- package/index.js +217 -207
- package/intelligence/index.js +2 -9
- package/intelligence/infer.js +10 -16
- package/intelligence/patterns.js +23 -18
- package/intelligence/proactive.js +16 -15
- package/intelligence/serendipity.js +57 -20
- package/memory.js +13 -8
- package/notify.js +39 -14
- package/package.json +27 -20
- package/presence.js +2 -2
- package/prompts.js +5 -9
- package/protocol/index.js +123 -87
- package/protocol/telegram-commands.js +36 -37
- package/store/api.js +358 -529
- package/store/local.js +9 -10
- package/store/profiles.js +48 -192
- package/store/reservations.js +2 -9
- package/store/skills.js +69 -71
- package/store/sqlite.js +355 -0
- package/tools/_actions.js +48 -387
- package/tools/_connection-queue.js +45 -56
- package/tools/_discovery-enhanced.js +52 -57
- package/tools/_discovery.js +87 -185
- package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
- package/tools/{shipback.js → _experimental/shipback.js} +4 -3
- package/tools/_proactive-discovery.js +60 -73
- package/tools/_shared/index.js +41 -64
- package/tools/admin-inbox.js +10 -15
- package/tools/agents.js +1 -1
- package/tools/artifact-create.js +13 -23
- package/tools/artifact-view.js +4 -4
- package/tools/{_deprecated/back.js → back.js} +1 -1
- package/tools/bye.js +3 -5
- package/tools/consent.js +2 -2
- package/tools/context.js +9 -10
- package/tools/crossword.js +3 -2
- package/tools/discover.js +94 -356
- package/tools/dm.js +27 -86
- package/tools/doctor.js +12 -41
- package/tools/drawing.js +34 -20
- package/tools/echo.js +11 -11
- package/tools/feed.js +30 -58
- package/tools/follow.js +64 -187
- package/tools/{_deprecated/forget.js → forget.js} +4 -7
- package/tools/game.js +144 -48
- package/tools/handoff.js +6 -8
- package/tools/help.js +3 -3
- package/tools/idea.js +15 -27
- package/tools/inbox.js +121 -293
- package/tools/init.js +54 -151
- package/tools/invite.js +8 -21
- package/tools/migrate.js +27 -24
- package/tools/multiplayer-game.js +50 -40
- package/tools/{_deprecated/mute.js → mute.js} +4 -3
- package/tools/notifications.js +58 -48
- package/tools/observe.js +12 -15
- package/tools/onboarding.js +8 -11
- package/tools/open.js +13 -144
- package/tools/party-game.js +23 -12
- package/tools/patterns.js +2 -1
- package/tools/ping.js +5 -7
- package/tools/react.js +28 -30
- package/tools/{_deprecated/recall.js → recall.js} +5 -10
- package/tools/release.js +4 -2
- package/tools/{_deprecated/remember.js → remember.js} +4 -6
- package/tools/report.js +2 -2
- package/tools/request.js +6 -26
- package/tools/reserve.js +1 -1
- package/tools/session-fork.js +97 -0
- package/tools/session-save.js +109 -0
- package/tools/settings.js +30 -99
- package/tools/ship.js +74 -56
- package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
- package/tools/social-inbox.js +22 -28
- package/tools/social-post.js +24 -27
- package/tools/solo-game.js +54 -46
- package/tools/start.js +14 -148
- package/tools/status.js +21 -68
- package/tools/submit.js +4 -2
- package/tools/suggest-tags.js +36 -33
- package/tools/summarize.js +19 -16
- package/tools/tag-suggestions.js +72 -73
- package/tools/test.js +1 -1
- package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
- package/tools/token.js +4 -4
- package/tools/update.js +1 -2
- package/tools/watch.js +132 -112
- package/tools/who.js +20 -40
- package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
- package/tools/workshop-buddy.js +52 -53
- package/tools/x-mentions.js +0 -1
- package/tools/x-reply.js +0 -1
- package/twitter.js +14 -20
- package/version.json +8 -10
- package/analytics.js +0 -107
- package/auth-store.js +0 -148
- package/auto-update.js +0 -130
- package/bridges/bridge-monitor.js +0 -388
- package/bridges/discord-bot.js +0 -431
- package/bridges/farcaster.js +0 -299
- package/bridges/telegram.js +0 -261
- package/bridges/webhook-health.js +0 -420
- package/bridges/webhook-server.js +0 -437
- package/bridges/whatsapp.js +0 -441
- package/bridges/x-webhook.js +0 -423
- package/games/arcade.js +0 -406
- package/games/chess.js +0 -451
- package/games/colorguess.js +0 -343
- package/games/crossword-words.js +0 -171
- package/games/crossword.js +0 -461
- package/games/drawing.js +0 -347
- package/games/gameroulette.js +0 -300
- package/games/gamerouter.js +0 -336
- package/games/gamestatus.js +0 -337
- package/games/guessnumber.js +0 -209
- package/games/hangman.js +0 -279
- package/games/memory.js +0 -338
- package/games/multiplayer-tictactoe.js +0 -389
- package/games/pixelart.js +0 -399
- package/games/quickduel.js +0 -354
- package/games/riddle.js +0 -371
- package/games/rockpaperscissors.js +0 -291
- package/games/snake.js +0 -406
- package/games/storybuilder.js +0 -343
- package/games/tictactoe.js +0 -345
- package/games/twentyquestions.js +0 -286
- package/games/twotruths.js +0 -207
- package/games/werewolf.js +0 -508
- package/games/wordassociation.js +0 -247
- package/games/wordchain.js +0 -135
- package/intelligence/interests.js +0 -369
- package/notification-emitter.js +0 -77
- package/setup.js +0 -480
- package/smart-inbox.js +0 -276
- package/tools/_deprecated/auto-suggest-connections.js +0 -304
- package/tools/_deprecated/bootstrap-skills.js +0 -231
- package/tools/_deprecated/bridge-dashboard.js +0 -342
- package/tools/_deprecated/bridge-health.js +0 -400
- package/tools/_deprecated/bridge-live.js +0 -384
- package/tools/_deprecated/bridges.js +0 -383
- package/tools/_deprecated/colorguess.js +0 -281
- package/tools/_deprecated/discover-insights.js +0 -379
- package/tools/_deprecated/discover-momentum.js +0 -256
- package/tools/_deprecated/discovery-analytics.js +0 -345
- package/tools/_deprecated/discovery-auto-suggest.js +0 -275
- package/tools/_deprecated/discovery-bootstrap.js +0 -267
- package/tools/_deprecated/discovery-daily.js +0 -375
- package/tools/_deprecated/discovery-dashboard.js +0 -385
- package/tools/_deprecated/discovery-digest.js +0 -314
- package/tools/_deprecated/discovery-hub.js +0 -357
- package/tools/_deprecated/discovery-insights.js +0 -384
- package/tools/_deprecated/discovery-momentum.js +0 -281
- package/tools/_deprecated/discovery-monitor.js +0 -319
- package/tools/_deprecated/discovery-proactive.js +0 -300
- package/tools/_deprecated/draw.js +0 -317
- package/tools/_deprecated/farcaster.js +0 -307
- package/tools/_deprecated/games-catalog.js +0 -376
- package/tools/_deprecated/games.js +0 -313
- package/tools/_deprecated/guessnumber.js +0 -194
- package/tools/_deprecated/hangman.js +0 -129
- package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
- package/tools/_deprecated/riddle.js +0 -240
- package/tools/_deprecated/run-bootstrap.js +0 -69
- package/tools/_deprecated/skills-analytics.js +0 -349
- package/tools/_deprecated/skills-bootstrap.js +0 -301
- package/tools/_deprecated/skills-dashboard.js +0 -268
- package/tools/_deprecated/skills.js +0 -380
- package/tools/_deprecated/smart-intro.js +0 -353
- package/tools/_deprecated/storybuilder.js +0 -331
- package/tools/_deprecated/telegram-bot.js +0 -183
- package/tools/_deprecated/telegram-setup.js +0 -214
- package/tools/_deprecated/twentyquestions.js +0 -143
- package/tools/_shared.js +0 -234
- package/tools/_work-context.js +0 -338
- package/tools/_work-context.manual-test.js +0 -199
- package/tools/_work-context.test.js +0 -260
- package/tools/activity.js +0 -220
- package/tools/agent-treasury.js +0 -288
- package/tools/analytics.js +0 -191
- package/tools/approve.js +0 -197
- package/tools/arcade.js +0 -173
- package/tools/artifacts-price.js +0 -107
- package/tools/ask-expert.js +0 -160
- package/tools/available.js +0 -120
- package/tools/become-expert.js +0 -150
- package/tools/broadcast.js +0 -325
- package/tools/chat.js +0 -202
- package/tools/collaborative-drawing.js +0 -286
- package/tools/connection-status.js +0 -178
- package/tools/earnings.js +0 -126
- package/tools/friends.js +0 -207
- package/tools/genesis.js +0 -233
- package/tools/gig-browse.js +0 -206
- package/tools/gig-complete.js +0 -144
- package/tools/health.js +0 -87
- package/tools/leaderboard.js +0 -117
- package/tools/lib/git-apply.js +0 -206
- package/tools/lib/git-bundle.js +0 -407
- package/tools/mint.js +0 -377
- package/tools/plan.js +0 -225
- package/tools/profile.js +0 -219
- package/tools/proof-of-work.js +0 -144
- package/tools/pulse.js +0 -218
- package/tools/reply.js +0 -166
- package/tools/reputation.js +0 -175
- package/tools/schedule.js +0 -367
- package/tools/search-messages.js +0 -123
- package/tools/session.js +0 -467
- package/tools/session_price.js +0 -128
- package/tools/smart-check.js +0 -201
- package/tools/social-processor.js +0 -445
- package/tools/streak.js +0 -147
- package/tools/stuck.js +0 -297
- package/tools/subscribe.js +0 -148
- package/tools/subscriptions.js +0 -134
- package/tools/tip.js +0 -193
- package/tools/wallet.js +0 -269
- package/tools/webhook-test.js +0 -388
- package/tools/withdraw.js +0 -145
- package/tools/work-summary.js +0 -96
- package/tools/workshop.js +0 -327
- /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
- /package/tools/{l2.js → _experimental/l2.js} +0 -0
- /package/tools/{_deprecated/away.js → away.js} +0 -0
|
@@ -15,17 +15,17 @@ const fetch = require('node-fetch');
|
|
|
15
15
|
// Chain configuration (matches lib/vibe-l2/config.js)
|
|
16
16
|
const CHAINS = {
|
|
17
17
|
'vibe-l2': {
|
|
18
|
-
chainId: 84532000,
|
|
18
|
+
chainId: 84532000, // Mainnet
|
|
19
19
|
name: 'VIBE L2',
|
|
20
20
|
rpcUrl: process.env.VIBE_L2_RPC || 'https://rpc.vibe.network',
|
|
21
|
-
explorerUrl: 'https://explorer.vibe.network'
|
|
21
|
+
explorerUrl: 'https://explorer.vibe.network'
|
|
22
22
|
},
|
|
23
23
|
'vibe-l2-testnet': {
|
|
24
|
-
chainId: 84532001,
|
|
24
|
+
chainId: 84532001, // Testnet
|
|
25
25
|
name: 'VIBE L2 Testnet',
|
|
26
26
|
rpcUrl: process.env.VIBE_L2_TESTNET_RPC || 'https://rpc-testnet.vibe.network',
|
|
27
|
-
explorerUrl: 'https://explorer-testnet.vibe.network'
|
|
28
|
-
}
|
|
27
|
+
explorerUrl: 'https://explorer-testnet.vibe.network'
|
|
28
|
+
}
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
const definition = {
|
|
@@ -48,60 +48,60 @@ const definition = {
|
|
|
48
48
|
|
|
49
49
|
async function handler(args) {
|
|
50
50
|
const { network = 'vibe-l2-testnet', verbose = false } = args;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
try {
|
|
52
|
+
const chain = CHAINS[network];
|
|
53
|
+
if (!chain) {
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: `Unknown network: ${network}`,
|
|
57
|
+
available: Object.keys(CHAINS)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
// Check RPC health
|
|
62
|
+
const rpcStatus = await checkRpcHealth(chain.rpcUrl);
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
// Get contract addresses from environment
|
|
65
|
+
// Standardized format: VIBE_ARTIFACTS_TESTNET or VIBE_ARTIFACTS_MAINNET
|
|
66
|
+
const envSuffix = network === 'vibe-l2' ? 'MAINNET' : 'TESTNET';
|
|
67
|
+
const contracts = {
|
|
68
|
+
vibeArtifacts: process.env[`VIBE_ARTIFACTS_${envSuffix}`],
|
|
69
|
+
shipbackRegistry: process.env[`SHIPBACK_REGISTRY_${envSuffix}`],
|
|
70
|
+
vibeToken: process.env[`VIBE_TOKEN_${envSuffix}`]
|
|
71
|
+
};
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
// Check Shipback API health
|
|
74
|
+
const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
75
|
+
let apiHealth = { status: 'unknown' };
|
|
76
|
+
try {
|
|
77
|
+
const apiResp = await fetch(`${apiUrl}/api/health?full=true`, { timeout: 5000 });
|
|
78
|
+
apiHealth = await apiResp.json();
|
|
79
|
+
} catch (e) {
|
|
80
|
+
apiHealth = { status: 'error', error: e.message };
|
|
81
|
+
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
83
|
+
// Build status report
|
|
84
|
+
const status = {
|
|
85
|
+
network: chain.name,
|
|
86
|
+
chainId: chain.chainId,
|
|
87
|
+
rpc: {
|
|
88
|
+
url: chain.rpcUrl,
|
|
89
|
+
...rpcStatus
|
|
90
|
+
},
|
|
91
|
+
contracts: {
|
|
92
|
+
vibeArtifacts: contracts.vibeArtifacts || 'not deployed',
|
|
93
|
+
shipbackRegistry: contracts.shipbackRegistry || 'not deployed',
|
|
94
|
+
vibeToken: contracts.vibeToken || 'not deployed (optional)'
|
|
95
|
+
},
|
|
96
|
+
api: apiHealth.status === 'healthy' ? 'healthy' : apiHealth.status,
|
|
97
|
+
explorer: chain.explorerUrl
|
|
98
|
+
};
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
// Format output
|
|
101
|
+
const rpcIcon = rpcStatus.healthy ? '🟢' : '🔴';
|
|
102
|
+
const apiIcon = apiHealth.status === 'healthy' ? '🟢' : '🟡';
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
let formatted = `
|
|
105
105
|
┌─────────────────────────────────────────────────────┐
|
|
106
106
|
│ VIBE L2 STATUS │
|
|
107
107
|
├─────────────────────────────────────────────────────┤
|
|
@@ -121,28 +121,27 @@ async function handler(args) {
|
|
|
121
121
|
└─────────────────────────────────────────────────────┘
|
|
122
122
|
`.trim();
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
if (verbose && rpcStatus.healthy) {
|
|
125
|
+
formatted += `\n\nDetailed RPC Info:
|
|
126
126
|
Latest Block: ${rpcStatus.blockNumber}
|
|
127
127
|
Block Time: ${rpcStatus.blockTime || 'unknown'}
|
|
128
128
|
Gas Price: ${rpcStatus.gasPrice} gwei
|
|
129
129
|
Chain ID: ${rpcStatus.chainId}`;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// Shipback info
|
|
133
|
-
formatted += `\n\n💰 Shipback Distribution: 80% creator / 15% protocol / 5% foundation`;
|
|
134
|
-
formatted += `\n📖 Explorer: ${chain.explorerUrl}`;
|
|
130
|
+
}
|
|
135
131
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
};
|
|
132
|
+
// Shipback info
|
|
133
|
+
formatted += `\n\n💰 Shipback Distribution: 80% creator / 15% protocol / 5% foundation`;
|
|
134
|
+
formatted += `\n📖 Explorer: ${chain.explorerUrl}`;
|
|
140
135
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
return {
|
|
137
|
+
display: formatted,
|
|
138
|
+
data: status
|
|
139
|
+
};
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return {
|
|
142
|
+
display: `❌ Failed to check L2 status: ${error.message}`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
146
145
|
}
|
|
147
146
|
|
|
148
147
|
/**
|
|
@@ -205,7 +204,6 @@ async function checkRpcHealth(rpcUrl) {
|
|
|
205
204
|
chainId,
|
|
206
205
|
latency: 'ok'
|
|
207
206
|
};
|
|
208
|
-
|
|
209
207
|
} catch (error) {
|
|
210
208
|
return {
|
|
211
209
|
healthy: false,
|
|
@@ -253,9 +253,10 @@ async function initiateClaim(apiUrl, handle, signature, nonce) {
|
|
|
253
253
|
const balanceData = await balanceResp.json();
|
|
254
254
|
|
|
255
255
|
if (!balanceData.canClaim) {
|
|
256
|
-
const reason =
|
|
257
|
-
|
|
258
|
-
|
|
256
|
+
const reason =
|
|
257
|
+
parseFloat(balanceData.pendingBalanceEth) < parseFloat(balanceData.minClaimAmountEth)
|
|
258
|
+
? `Balance (${balanceData.pendingBalanceEth} ETH) below minimum (${balanceData.minClaimAmountEth} ETH)`
|
|
259
|
+
: `Cooldown active until ${balanceData.cooldownEndsAt}`;
|
|
259
260
|
|
|
260
261
|
return {
|
|
261
262
|
display: `❌ Cannot claim yet: ${reason}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Proactive Discovery Engine — Smart background matching for offline users
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* This runs when users aren't online to:
|
|
5
5
|
* - Analyze recent ships and suggest collaborations
|
|
6
6
|
* - Pre-compute high-quality matches for when users return
|
|
@@ -16,19 +16,19 @@ const { formatTimeAgo } = require('./_shared');
|
|
|
16
16
|
async function analyzeShippingPatterns() {
|
|
17
17
|
const profiles = await userProfiles.getAllProfiles();
|
|
18
18
|
const now = Date.now();
|
|
19
|
-
const twoWeeksAgo = now -
|
|
20
|
-
|
|
19
|
+
const twoWeeksAgo = now - 14 * 24 * 60 * 60 * 1000;
|
|
20
|
+
|
|
21
21
|
const patterns = {
|
|
22
22
|
recentShippers: [],
|
|
23
23
|
complementaryShips: [],
|
|
24
24
|
similarProjects: [],
|
|
25
25
|
emergingTrends: {}
|
|
26
26
|
};
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
// Find users who shipped recently
|
|
29
29
|
for (const profile of profiles) {
|
|
30
30
|
if (!profile.ships || profile.ships.length === 0) continue;
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
const recentShips = profile.ships.filter(s => s.timestamp > twoWeeksAgo);
|
|
33
33
|
if (recentShips.length > 0) {
|
|
34
34
|
patterns.recentShippers.push({
|
|
@@ -40,7 +40,7 @@ async function analyzeShippingPatterns() {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
// Find complementary ships (e.g., one shipped frontend, another backend)
|
|
45
45
|
const complementaryPairs = [
|
|
46
46
|
{ keywords: ['frontend', 'ui', 'react', 'vue'], complement: ['backend', 'api', 'server', 'database'] },
|
|
@@ -49,24 +49,16 @@ async function analyzeShippingPatterns() {
|
|
|
49
49
|
{ keywords: ['mobile', 'app', 'ios', 'android'], complement: ['backend', 'api'] },
|
|
50
50
|
{ keywords: ['smart contract', 'solidity', 'web3'], complement: ['frontend', 'dapp'] }
|
|
51
51
|
];
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
for (const pair of complementaryPairs) {
|
|
54
|
-
const primaryShippers = patterns.recentShippers.filter(s =>
|
|
55
|
-
s.ships.some(ship =>
|
|
56
|
-
pair.keywords.some(keyword =>
|
|
57
|
-
ship.what.toLowerCase().includes(keyword)
|
|
58
|
-
)
|
|
59
|
-
)
|
|
54
|
+
const primaryShippers = patterns.recentShippers.filter(s =>
|
|
55
|
+
s.ships.some(ship => pair.keywords.some(keyword => ship.what.toLowerCase().includes(keyword)))
|
|
60
56
|
);
|
|
61
|
-
|
|
62
|
-
const complementShippers = patterns.recentShippers.filter(s =>
|
|
63
|
-
s.ships.some(ship =>
|
|
64
|
-
pair.complement.some(keyword =>
|
|
65
|
-
ship.what.toLowerCase().includes(keyword)
|
|
66
|
-
)
|
|
67
|
-
)
|
|
57
|
+
|
|
58
|
+
const complementShippers = patterns.recentShippers.filter(s =>
|
|
59
|
+
s.ships.some(ship => pair.complement.some(keyword => ship.what.toLowerCase().includes(keyword)))
|
|
68
60
|
);
|
|
69
|
-
|
|
61
|
+
|
|
70
62
|
if (primaryShippers.length > 0 && complementShippers.length > 0) {
|
|
71
63
|
patterns.complementaryShips.push({
|
|
72
64
|
type: `${pair.keywords[0]}/${pair.complement[0]}`,
|
|
@@ -75,15 +67,17 @@ async function analyzeShippingPatterns() {
|
|
|
75
67
|
});
|
|
76
68
|
}
|
|
77
69
|
}
|
|
78
|
-
|
|
70
|
+
|
|
79
71
|
// Find similar project patterns
|
|
80
72
|
const projectKeywords = {};
|
|
81
73
|
for (const shipper of patterns.recentShippers) {
|
|
82
74
|
for (const ship of shipper.ships) {
|
|
83
|
-
const words = ship.what
|
|
75
|
+
const words = ship.what
|
|
76
|
+
.toLowerCase()
|
|
77
|
+
.split(/\s+/)
|
|
84
78
|
.filter(word => word.length > 3)
|
|
85
79
|
.filter(word => !['the', 'and', 'for', 'with', 'that', 'this'].includes(word));
|
|
86
|
-
|
|
80
|
+
|
|
87
81
|
for (const word of words) {
|
|
88
82
|
if (!projectKeywords[word]) projectKeywords[word] = [];
|
|
89
83
|
projectKeywords[word].push({
|
|
@@ -94,7 +88,7 @@ async function analyzeShippingPatterns() {
|
|
|
94
88
|
}
|
|
95
89
|
}
|
|
96
90
|
}
|
|
97
|
-
|
|
91
|
+
|
|
98
92
|
// Find keywords with multiple recent ships (emerging trends)
|
|
99
93
|
for (const [keyword, ships] of Object.entries(projectKeywords)) {
|
|
100
94
|
if (ships.length >= 2 && keyword !== 'app' && keyword !== 'website') {
|
|
@@ -104,7 +98,7 @@ async function analyzeShippingPatterns() {
|
|
|
104
98
|
};
|
|
105
99
|
}
|
|
106
100
|
}
|
|
107
|
-
|
|
101
|
+
|
|
108
102
|
return patterns;
|
|
109
103
|
}
|
|
110
104
|
|
|
@@ -112,36 +106,31 @@ async function analyzeShippingPatterns() {
|
|
|
112
106
|
async function preComputeMatches() {
|
|
113
107
|
const profiles = await userProfiles.getAllProfiles();
|
|
114
108
|
const now = Date.now();
|
|
115
|
-
const oneWeekAgo = now -
|
|
116
|
-
|
|
109
|
+
const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000;
|
|
110
|
+
|
|
117
111
|
// Find users who haven't been active but have good profiles
|
|
118
|
-
const dormantUsers = profiles.filter(
|
|
119
|
-
p.lastSeen && p.lastSeen < oneWeekAgo &&
|
|
120
|
-
p.building && p.interests?.length > 0
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const activeUsers = profiles.filter(p =>
|
|
124
|
-
p.lastSeen && p.lastSeen > oneWeekAgo
|
|
112
|
+
const dormantUsers = profiles.filter(
|
|
113
|
+
p => p.lastSeen && p.lastSeen < oneWeekAgo && p.building && p.interests?.length > 0
|
|
125
114
|
);
|
|
126
|
-
|
|
115
|
+
|
|
116
|
+
const activeUsers = profiles.filter(p => p.lastSeen && p.lastSeen > oneWeekAgo);
|
|
117
|
+
|
|
127
118
|
const preComputedMatches = [];
|
|
128
|
-
|
|
119
|
+
|
|
129
120
|
for (const dormantUser of dormantUsers) {
|
|
130
121
|
const potentialMatches = [];
|
|
131
|
-
|
|
122
|
+
|
|
132
123
|
for (const activeUser of activeUsers) {
|
|
133
124
|
if (dormantUser.handle === activeUser.handle) continue;
|
|
134
|
-
|
|
125
|
+
|
|
135
126
|
// Check if already connected
|
|
136
|
-
const alreadyConnected = await userProfiles.hasBeenConnected(
|
|
137
|
-
dormantUser.handle,
|
|
138
|
-
activeUser.handle
|
|
139
|
-
);
|
|
127
|
+
const alreadyConnected = await userProfiles.hasBeenConnected(dormantUser.handle, activeUser.handle);
|
|
140
128
|
if (alreadyConnected) continue;
|
|
141
|
-
|
|
129
|
+
|
|
142
130
|
// Calculate match score using existing algorithm
|
|
143
131
|
const match = calculateSimpleMatchScore(dormantUser, activeUser);
|
|
144
|
-
if (match.score > 15) {
|
|
132
|
+
if (match.score > 15) {
|
|
133
|
+
// Higher threshold for dormant users
|
|
145
134
|
potentialMatches.push({
|
|
146
135
|
handle: activeUser.handle,
|
|
147
136
|
score: match.score,
|
|
@@ -150,7 +139,7 @@ async function preComputeMatches() {
|
|
|
150
139
|
});
|
|
151
140
|
}
|
|
152
141
|
}
|
|
153
|
-
|
|
142
|
+
|
|
154
143
|
if (potentialMatches.length > 0) {
|
|
155
144
|
preComputedMatches.push({
|
|
156
145
|
dormantUser: dormantUser.handle,
|
|
@@ -159,7 +148,7 @@ async function preComputeMatches() {
|
|
|
159
148
|
});
|
|
160
149
|
}
|
|
161
150
|
}
|
|
162
|
-
|
|
151
|
+
|
|
163
152
|
return preComputedMatches;
|
|
164
153
|
}
|
|
165
154
|
|
|
@@ -167,7 +156,7 @@ async function preComputeMatches() {
|
|
|
167
156
|
function calculateSimpleMatchScore(user1, user2) {
|
|
168
157
|
let score = 0;
|
|
169
158
|
const reasons = [];
|
|
170
|
-
|
|
159
|
+
|
|
171
160
|
// Interest overlap
|
|
172
161
|
if (user1.interests && user2.interests) {
|
|
173
162
|
const shared = user1.interests.filter(i => user2.interests.includes(i));
|
|
@@ -176,8 +165,8 @@ function calculateSimpleMatchScore(user1, user2) {
|
|
|
176
165
|
reasons.push(`Shared interests: ${shared.slice(0, 2).join(', ')}`);
|
|
177
166
|
}
|
|
178
167
|
}
|
|
179
|
-
|
|
180
|
-
// Tag overlap
|
|
168
|
+
|
|
169
|
+
// Tag overlap
|
|
181
170
|
if (user1.tags && user2.tags) {
|
|
182
171
|
const sharedTags = user1.tags.filter(t => user2.tags.includes(t));
|
|
183
172
|
if (sharedTags.length > 0) {
|
|
@@ -185,7 +174,7 @@ function calculateSimpleMatchScore(user1, user2) {
|
|
|
185
174
|
reasons.push(`Similar skills: ${sharedTags.slice(0, 2).join(', ')}`);
|
|
186
175
|
}
|
|
187
176
|
}
|
|
188
|
-
|
|
177
|
+
|
|
189
178
|
// Building similarity
|
|
190
179
|
if (user1.building && user2.building) {
|
|
191
180
|
const building1 = user1.building.toLowerCase();
|
|
@@ -198,7 +187,7 @@ function calculateSimpleMatchScore(user1, user2) {
|
|
|
198
187
|
reasons.push(`Both working on ${overlap[0]}`);
|
|
199
188
|
}
|
|
200
189
|
}
|
|
201
|
-
|
|
190
|
+
|
|
202
191
|
return { score, reasons: reasons.slice(0, 2) };
|
|
203
192
|
}
|
|
204
193
|
|
|
@@ -206,21 +195,21 @@ function calculateSimpleMatchScore(user1, user2) {
|
|
|
206
195
|
async function identifyEmergingClusters() {
|
|
207
196
|
const profiles = await userProfiles.getAllProfiles();
|
|
208
197
|
const now = Date.now();
|
|
209
|
-
const oneMonthAgo = now -
|
|
210
|
-
|
|
198
|
+
const oneMonthAgo = now - 30 * 24 * 60 * 60 * 1000;
|
|
199
|
+
|
|
211
200
|
// Only consider recent users for emerging trends
|
|
212
201
|
const recentProfiles = profiles.filter(p => p.firstSeen && p.firstSeen > oneMonthAgo);
|
|
213
|
-
|
|
202
|
+
|
|
214
203
|
if (recentProfiles.length < 3) {
|
|
215
204
|
return { message: 'Not enough recent users to identify emerging clusters' };
|
|
216
205
|
}
|
|
217
|
-
|
|
206
|
+
|
|
218
207
|
const clusters = {};
|
|
219
|
-
|
|
208
|
+
|
|
220
209
|
// Group by interests
|
|
221
210
|
for (const profile of recentProfiles) {
|
|
222
211
|
if (!profile.interests || profile.interests.length === 0) continue;
|
|
223
|
-
|
|
212
|
+
|
|
224
213
|
for (const interest of profile.interests) {
|
|
225
214
|
if (!clusters[interest]) {
|
|
226
215
|
clusters[interest] = [];
|
|
@@ -228,7 +217,7 @@ async function identifyEmergingClusters() {
|
|
|
228
217
|
clusters[interest].push(profile.handle);
|
|
229
218
|
}
|
|
230
219
|
}
|
|
231
|
-
|
|
220
|
+
|
|
232
221
|
// Find clusters with 2+ recent users (potential communities)
|
|
233
222
|
const emergingClusters = Object.entries(clusters)
|
|
234
223
|
.filter(([interest, handles]) => handles.length >= 2)
|
|
@@ -239,7 +228,7 @@ async function identifyEmergingClusters() {
|
|
|
239
228
|
potential: handles.length >= 3 ? 'high' : 'medium'
|
|
240
229
|
}))
|
|
241
230
|
.sort((a, b) => b.size - a.size);
|
|
242
|
-
|
|
231
|
+
|
|
243
232
|
return emergingClusters;
|
|
244
233
|
}
|
|
245
234
|
|
|
@@ -247,17 +236,17 @@ async function identifyEmergingClusters() {
|
|
|
247
236
|
async function suggestOptimalTiming() {
|
|
248
237
|
const profiles = await userProfiles.getAllProfiles();
|
|
249
238
|
const now = Date.now();
|
|
250
|
-
|
|
239
|
+
|
|
251
240
|
// Analyze when users are typically active
|
|
252
241
|
const activityPatterns = {};
|
|
253
|
-
|
|
242
|
+
|
|
254
243
|
for (const profile of profiles) {
|
|
255
244
|
if (!profile.lastSeen) continue;
|
|
256
|
-
|
|
245
|
+
|
|
257
246
|
const lastSeenDate = new Date(profile.lastSeen);
|
|
258
247
|
const hour = lastSeenDate.getHours();
|
|
259
248
|
const dayOfWeek = lastSeenDate.getDay();
|
|
260
|
-
|
|
249
|
+
|
|
261
250
|
if (!activityPatterns[profile.handle]) {
|
|
262
251
|
activityPatterns[profile.handle] = {
|
|
263
252
|
preferredHours: [],
|
|
@@ -265,11 +254,11 @@ async function suggestOptimalTiming() {
|
|
|
265
254
|
timezone: null // Could be inferred from activity patterns
|
|
266
255
|
};
|
|
267
256
|
}
|
|
268
|
-
|
|
257
|
+
|
|
269
258
|
activityPatterns[profile.handle].preferredHours.push(hour);
|
|
270
259
|
activityPatterns[profile.handle].preferredDays.push(dayOfWeek);
|
|
271
260
|
}
|
|
272
|
-
|
|
261
|
+
|
|
273
262
|
// Find common active hours across users
|
|
274
263
|
const hourCounts = {};
|
|
275
264
|
for (const pattern of Object.values(activityPatterns)) {
|
|
@@ -277,18 +266,16 @@ async function suggestOptimalTiming() {
|
|
|
277
266
|
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
|
|
278
267
|
}
|
|
279
268
|
}
|
|
280
|
-
|
|
269
|
+
|
|
281
270
|
const peakHours = Object.entries(hourCounts)
|
|
282
|
-
.sort(([,a], [,b]) => b - a)
|
|
271
|
+
.sort(([, a], [, b]) => b - a)
|
|
283
272
|
.slice(0, 3)
|
|
284
273
|
.map(([hour, count]) => ({ hour: parseInt(hour), userCount: count }));
|
|
285
|
-
|
|
274
|
+
|
|
286
275
|
return {
|
|
287
276
|
peakActivityHours: peakHours,
|
|
288
277
|
totalActiveUsers: Object.keys(activityPatterns).length,
|
|
289
|
-
recommendedConnectionTimes: peakHours.map(p =>
|
|
290
|
-
`${p.hour}:00 (${p.userCount} users typically active)`
|
|
291
|
-
)
|
|
278
|
+
recommendedConnectionTimes: peakHours.map(p => `${p.hour}:00 (${p.userCount} users typically active)`)
|
|
292
279
|
};
|
|
293
280
|
}
|
|
294
281
|
|
|
@@ -298,4 +285,4 @@ module.exports = {
|
|
|
298
285
|
identifyEmergingClusters,
|
|
299
286
|
suggestOptimalTiming,
|
|
300
287
|
calculateSimpleMatchScore
|
|
301
|
-
};
|
|
288
|
+
};
|
package/tools/_shared/index.js
CHANGED
|
@@ -7,70 +7,34 @@
|
|
|
7
7
|
* - Time formatting
|
|
8
8
|
* - Display formatting
|
|
9
9
|
* - Error handling
|
|
10
|
-
* - Relevancy API fetching
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
|
-
const config = require('../../config');
|
|
14
|
-
|
|
15
|
-
// ============ RELEVANCY API ============
|
|
16
|
-
|
|
17
|
-
const RELEVANCY_API_TIMEOUT = 5000; // 5 seconds
|
|
18
|
-
|
|
19
12
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @
|
|
23
|
-
* @
|
|
24
|
-
* @
|
|
25
|
-
* @
|
|
13
|
+
* @typedef {Object} ToolResult
|
|
14
|
+
* @property {string} [display] - Formatted text to show the user
|
|
15
|
+
* @property {string} [hint] - Hint for Claude about next action
|
|
16
|
+
* @property {Object} [suggestion] - Suggested next action
|
|
17
|
+
* @property {number} [unread_count] - Unread message count
|
|
18
|
+
* @property {string} [for_handle] - Handle this result is relevant to
|
|
26
19
|
*/
|
|
27
|
-
async function fetchRelevantUsers(handle, context = 'discovery', limit = 5) {
|
|
28
|
-
try {
|
|
29
|
-
const apiUrl = config.getApiUrl();
|
|
30
|
-
const controller = new AbortController();
|
|
31
|
-
const timeout = setTimeout(() => controller.abort(), RELEVANCY_API_TIMEOUT);
|
|
32
|
-
|
|
33
|
-
const url = `${apiUrl}/api/relevancy?handle=${encodeURIComponent(handle)}&context=${context}&limit=${limit}`;
|
|
34
|
-
|
|
35
|
-
const response = await fetch(url, {
|
|
36
|
-
method: 'GET',
|
|
37
|
-
headers: {
|
|
38
|
-
'Accept': 'application/json',
|
|
39
|
-
'User-Agent': 'vibe-mcp-client'
|
|
40
|
-
},
|
|
41
|
-
signal: controller.signal
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
clearTimeout(timeout);
|
|
45
|
-
|
|
46
|
-
if (!response.ok) {
|
|
47
|
-
console.log(`[relevancy] API returned ${response.status}`);
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
20
|
|
|
51
|
-
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} ToolDefinition
|
|
23
|
+
* @property {string} name - Tool name (vibe_*)
|
|
24
|
+
* @property {string} description - Human-readable description
|
|
25
|
+
* @property {Object} inputSchema - JSON Schema for tool arguments
|
|
26
|
+
* @property {string} inputSchema.type - Always "object"
|
|
27
|
+
* @property {Object} inputSchema.properties - Argument definitions
|
|
28
|
+
* @property {string[]} [inputSchema.required] - Required argument names
|
|
29
|
+
*/
|
|
52
30
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} ToolModule
|
|
33
|
+
* @property {ToolDefinition} definition - Tool schema definition
|
|
34
|
+
* @property {function(Object): Promise<ToolResult>} handler - Tool handler function
|
|
35
|
+
*/
|
|
57
36
|
|
|
58
|
-
|
|
59
|
-
matches: data.matches,
|
|
60
|
-
tier: data.tier,
|
|
61
|
-
socialStats: data.socialStats,
|
|
62
|
-
fromApi: true
|
|
63
|
-
};
|
|
64
|
-
} catch (e) {
|
|
65
|
-
// Log but don't fail - caller should handle null gracefully
|
|
66
|
-
if (e.name === 'AbortError') {
|
|
67
|
-
console.log('[relevancy] API timeout');
|
|
68
|
-
} else {
|
|
69
|
-
console.log('[relevancy] API error:', e.message);
|
|
70
|
-
}
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
37
|
+
const config = require('../../config');
|
|
74
38
|
|
|
75
39
|
// ============ INIT CHECK ============
|
|
76
40
|
|
|
@@ -93,7 +57,7 @@ function requireInit() {
|
|
|
93
57
|
* @returns {Function} - Wrapped handler
|
|
94
58
|
*/
|
|
95
59
|
function withInit(handler) {
|
|
96
|
-
return async function(args) {
|
|
60
|
+
return async function (args) {
|
|
97
61
|
const initCheck = requireInit();
|
|
98
62
|
if (initCheck) return initCheck;
|
|
99
63
|
return handler(args);
|
|
@@ -245,7 +209,7 @@ function error(message) {
|
|
|
245
209
|
* @returns {Function} - Wrapped handler with try/catch
|
|
246
210
|
*/
|
|
247
211
|
function withErrorHandling(handler) {
|
|
248
|
-
return async function(args) {
|
|
212
|
+
return async function (args) {
|
|
249
213
|
try {
|
|
250
214
|
return await handler(args);
|
|
251
215
|
} catch (e) {
|
|
@@ -262,7 +226,7 @@ function withErrorHandling(handler) {
|
|
|
262
226
|
* @returns {Function} - Combined wrapper
|
|
263
227
|
*/
|
|
264
228
|
function compose(...wrappers) {
|
|
265
|
-
return function(handler) {
|
|
229
|
+
return function (handler) {
|
|
266
230
|
return wrappers.reduceRight((h, wrapper) => wrapper(h), handler);
|
|
267
231
|
};
|
|
268
232
|
}
|
|
@@ -291,10 +255,20 @@ function validateRequired(args, required) {
|
|
|
291
255
|
return null;
|
|
292
256
|
}
|
|
293
257
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
258
|
+
// ============ DEBUG ============
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Debug logging — only outputs when VIBE_DEBUG=true
|
|
262
|
+
* @param {string} tag - Log tag (e.g., 'DM', 'GAME')
|
|
263
|
+
* @param {...any} args - Values to log
|
|
264
|
+
*/
|
|
265
|
+
function debug(tag, ...args) {
|
|
266
|
+
if (process.env.VIBE_DEBUG === 'true') {
|
|
267
|
+
console.error(`[vibe:${tag}]`, ...args);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
297
270
|
|
|
271
|
+
module.exports = {
|
|
298
272
|
// Init
|
|
299
273
|
requireInit,
|
|
300
274
|
withInit,
|
|
@@ -322,5 +296,8 @@ module.exports = {
|
|
|
322
296
|
withDefaults,
|
|
323
297
|
|
|
324
298
|
// Validation
|
|
325
|
-
validateRequired
|
|
299
|
+
validateRequired,
|
|
300
|
+
|
|
301
|
+
// Debug
|
|
302
|
+
debug
|
|
326
303
|
};
|