slashvibe-mcp 0.5.10 → 0.5.11
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/package.json +1 -1
- package/store/api.js +3 -1
- package/tools/_shared.js +89 -1
- package/tools/dm.js +5 -1
- package/tools/start.js +6 -0
- package/tools/who.js +17 -1
- package/version.json +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slashvibe-mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.11",
|
|
4
4
|
"mcpName": "io.github.vibecodinginc/vibe",
|
|
5
5
|
"description": "Social layer for Claude Code - DMs, presence, Matrix multiplayer rooms, and connection between AI-assisted developers",
|
|
6
6
|
"main": "index.js",
|
package/store/api.js
CHANGED
|
@@ -352,7 +352,9 @@ async function getActiveUsers() {
|
|
|
352
352
|
awayAt: u.awayAt || u.context?.awayAt || null,
|
|
353
353
|
// v2 additions
|
|
354
354
|
isLive: u.isLive || false,
|
|
355
|
-
isAgent: u.isAgent || false
|
|
355
|
+
isAgent: u.isAgent || false,
|
|
356
|
+
// Presence texture (server-enriched): most-recent ship
|
|
357
|
+
lastShip: u.lastShip || null
|
|
356
358
|
}));
|
|
357
359
|
|
|
358
360
|
// Sync presence data to local profiles (non-blocking)
|
package/tools/_shared.js
CHANGED
|
@@ -202,6 +202,88 @@ async function fetchRelevantUsers(handle, context = 'dm_suggest', limit = 5) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
// ─────────────────────────────────────────────────────────────
|
|
206
|
+
// First-DM activation (dormant users)
|
|
207
|
+
// ─────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Has this user ever sent a DM?
|
|
211
|
+
* Durable signal: a persisted `firstDmSent` flag (set on first successful send
|
|
212
|
+
* via markFirstDmSent), seeded by the presence of any conversation memory —
|
|
213
|
+
* existing messagers almost always have memory threads, so they're pre-excluded
|
|
214
|
+
* and never see the nudge. Self-heals: the moment they send one DM, the flag
|
|
215
|
+
* flips and the nudge is gone for good.
|
|
216
|
+
*
|
|
217
|
+
* @param {Array} threads - result of memory.listThreads()
|
|
218
|
+
* @returns {boolean}
|
|
219
|
+
*/
|
|
220
|
+
function hasEverMessaged(threads) {
|
|
221
|
+
try {
|
|
222
|
+
const cfg = config.load();
|
|
223
|
+
if (cfg && cfg.firstDmSent) return true;
|
|
224
|
+
} catch (e) {}
|
|
225
|
+
return Array.isArray(threads) && threads.length > 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Persist the "has sent a first DM" flag so the activation nudge stops.
|
|
230
|
+
* Called from dm.js on a successful send. Cheap, idempotent, best-effort.
|
|
231
|
+
*/
|
|
232
|
+
function markFirstDmSent() {
|
|
233
|
+
try {
|
|
234
|
+
const cfg = config.load();
|
|
235
|
+
if (!cfg || !cfg.firstDmSent) config.save({ firstDmSent: true });
|
|
236
|
+
} catch (e) {}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Pick the most-recently-active real human from a presence list, with a
|
|
241
|
+
* ready-to-paste opener. Filters out agents (is_agent) and self. Warms the
|
|
242
|
+
* opener from the target's most recent ship (lastShip) when available.
|
|
243
|
+
*
|
|
244
|
+
* @param {Array} others - active users (already excludes self by convention)
|
|
245
|
+
* @returns {{handle: string, opener: string}|null}
|
|
246
|
+
*/
|
|
247
|
+
function pickDormantTarget(others) {
|
|
248
|
+
const humans = (others || []).filter((u) => u && u.handle && !u.is_agent);
|
|
249
|
+
if (humans.length === 0) return null;
|
|
250
|
+
humans.sort(
|
|
251
|
+
(a, b) => new Date(b.lastSeen || b.last_seen || 0) - new Date(a.lastSeen || a.last_seen || 0)
|
|
252
|
+
);
|
|
253
|
+
const pick = humans[0];
|
|
254
|
+
// The opener is pasted inside a double-quoted `vibe dm @x "..."` command, so
|
|
255
|
+
// strip any double quotes/backslashes the ship text itself contains (ship
|
|
256
|
+
// content is arbitrary) and single-quote the snippet — otherwise an embedded
|
|
257
|
+
// quote closes the command early and breaks the paste.
|
|
258
|
+
const rawShip = pick.lastShip && pick.lastShip.what;
|
|
259
|
+
const shipped = rawShip ? String(rawShip).replace(/["\\]/g, '').trim().slice(0, 48) : '';
|
|
260
|
+
const opener = shipped
|
|
261
|
+
? `hey! saw you shipped '${shipped}' — what are you building?`
|
|
262
|
+
: `hey! what are you building?`;
|
|
263
|
+
return { handle: pick.handle, opener };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Render the first-DM activation nudge, or '' when it shouldn't show.
|
|
268
|
+
* Shows only for users who have never sent a DM, and only when a real human is
|
|
269
|
+
* around right now to receive it. This is the returning-user twin of the
|
|
270
|
+
* new-user welcome first-message pull — it reaches the dormant existing users
|
|
271
|
+
* where the actual funnel is.
|
|
272
|
+
*
|
|
273
|
+
* @param {Array} others - active users (excludes self)
|
|
274
|
+
* @param {Array} threads - memory.listThreads()
|
|
275
|
+
* @returns {string} markdown block (leading blank line) or ''
|
|
276
|
+
*/
|
|
277
|
+
function firstDmNudge(others, threads) {
|
|
278
|
+
if (hasEverMessaged(threads)) return '';
|
|
279
|
+
const target = pickDormantTarget(others);
|
|
280
|
+
if (!target) return '';
|
|
281
|
+
return (
|
|
282
|
+
`\n\n**👋 You haven't messaged anyone yet — @${target.handle} is around right now:**` +
|
|
283
|
+
`\n vibe dm @${target.handle} "${target.opener}"`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
205
287
|
// ─────────────────────────────────────────────────────────────
|
|
206
288
|
// Exports
|
|
207
289
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -230,5 +312,11 @@ module.exports = {
|
|
|
230
312
|
error,
|
|
231
313
|
|
|
232
314
|
// API
|
|
233
|
-
fetchRelevantUsers
|
|
315
|
+
fetchRelevantUsers,
|
|
316
|
+
|
|
317
|
+
// First-DM activation (dormant users)
|
|
318
|
+
hasEverMessaged,
|
|
319
|
+
markFirstDmSent,
|
|
320
|
+
pickDormantTarget,
|
|
321
|
+
firstDmNudge
|
|
234
322
|
};
|
package/tools/dm.js
CHANGED
|
@@ -8,7 +8,7 @@ const memory = require('../memory');
|
|
|
8
8
|
const userProfiles = require('../store/profiles');
|
|
9
9
|
const patterns = require('../intelligence/patterns');
|
|
10
10
|
const { trackMessage, checkBurst } = require('./summarize');
|
|
11
|
-
const { requireInit, normalizeHandle, truncate, warning, fetchRelevantUsers } = require('./_shared');
|
|
11
|
+
const { requireInit, normalizeHandle, truncate, warning, fetchRelevantUsers, markFirstDmSent } = require('./_shared');
|
|
12
12
|
const { actions, formatActions } = require('./_actions');
|
|
13
13
|
|
|
14
14
|
const definition = {
|
|
@@ -110,6 +110,10 @@ async function handler(args) {
|
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
// Mark that this user has sent a DM — silences the first-DM activation nudge
|
|
114
|
+
// (vibe start / vibe who) for good. Durable, idempotent, best-effort.
|
|
115
|
+
markFirstDmSent();
|
|
116
|
+
|
|
113
117
|
// Log social pattern (quietly, in background)
|
|
114
118
|
patterns.logMessageSent(them);
|
|
115
119
|
|
package/tools/start.js
CHANGED
|
@@ -17,6 +17,7 @@ const memory = require('../memory');
|
|
|
17
17
|
const notify = require('../notify');
|
|
18
18
|
const patterns = require('../intelligence/patterns');
|
|
19
19
|
const { actions, formatActions } = require('./_actions');
|
|
20
|
+
const { firstDmNudge } = require('./_shared');
|
|
20
21
|
const init = require('./init');
|
|
21
22
|
const { gatherWithTimeout } = require('./_work-context');
|
|
22
23
|
|
|
@@ -330,6 +331,11 @@ async function handler(args) {
|
|
|
330
331
|
}
|
|
331
332
|
}
|
|
332
333
|
|
|
334
|
+
// First-DM activation nudge (dormant users only): if this user has never sent
|
|
335
|
+
// a DM, point them at a real human who's around right now with a ready opener.
|
|
336
|
+
// Gated inside firstDmNudge so we never nag people who already message.
|
|
337
|
+
display += firstDmNudge(others, threads);
|
|
338
|
+
|
|
333
339
|
// Add unread messages section (if any)
|
|
334
340
|
if (unreadCount > 0) {
|
|
335
341
|
const unreadSenders = inboxThreads.filter(t => t.unread > 0);
|
package/tools/who.js
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
const config = require('../config');
|
|
11
11
|
const store = require('../store');
|
|
12
12
|
const notify = require('../notify');
|
|
13
|
-
const { formatTimeAgo, requireInit } = require('./_shared');
|
|
13
|
+
const { formatTimeAgo, requireInit, firstDmNudge } = require('./_shared');
|
|
14
|
+
const memory = require('../memory');
|
|
14
15
|
const { actions, formatActions } = require('./_actions');
|
|
15
16
|
const { enhanceUsersWithInference } = require('../intelligence/infer');
|
|
16
17
|
const { getTopSerendipity, getAllSerendipity } = require('../intelligence/serendipity');
|
|
@@ -225,10 +226,25 @@ _Check back in a bit — builders come and go._`
|
|
|
225
226
|
display += ` ${operatorTag}\n`;
|
|
226
227
|
}
|
|
227
228
|
display += ` ${activity}\n`;
|
|
229
|
+
// Presence texture: what they last shipped
|
|
230
|
+
if (u.lastShip && u.lastShip.what) {
|
|
231
|
+
display += ` _last shipped: "${u.lastShip.what}" ${u.lastShip.ago}_\n`;
|
|
232
|
+
}
|
|
228
233
|
display += ` _${timeAgo}_\n\n`;
|
|
229
234
|
});
|
|
230
235
|
}
|
|
231
236
|
|
|
237
|
+
// First-DM activation nudge (dormant users only): if signed in and this user
|
|
238
|
+
// has never sent a DM, point them at a real human who's active right now.
|
|
239
|
+
// firstDmNudge self-gates so people who already message never see it.
|
|
240
|
+
if (isInit) {
|
|
241
|
+
let threads = [];
|
|
242
|
+
try { threads = memory.listThreads(); } catch (e) {}
|
|
243
|
+
const activeOthers = active.filter(u => u.handle !== myHandle);
|
|
244
|
+
const nudge = firstDmNudge(activeOthers, threads);
|
|
245
|
+
if (nudge) display += nudge.replace(/^\n+/, '') + `\n\n`;
|
|
246
|
+
}
|
|
247
|
+
|
|
232
248
|
// Away section (expanded with messages if present)
|
|
233
249
|
if (away.length > 0) {
|
|
234
250
|
display += `---\n\n`;
|
package/version.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.5.
|
|
2
|
+
"version": "0.5.11",
|
|
3
3
|
"updated": "2026-07-03",
|
|
4
|
-
"changelog": "
|
|
4
|
+
"changelog": "Never messaged anyone yet? `vibe start` and `vibe who` now nudge you toward a real human who's around right now — a one-tap, ready-to-paste `vibe dm` with an opener warmed by what they just shipped. It shows only until you send your first DM, then it's gone for good. The whole point of the green dot is that someone's actually there; this closes the gap between seeing that and saying hi.",
|
|
5
5
|
"features": [
|
|
6
|
+
"First-DM nudge — if you've never messaged anyone, vibe start/who surface the most-recently-active real human as a one-tap opener, warmed by their latest ship. Self-heals: disappears the moment you send your first DM",
|
|
6
7
|
"vibe_play — the open primitive: pass any JSON state + a note to another viber, read it back to take your turn, and improvise any activity two sessions can dream up",
|
|
7
8
|
"vibe_corpse — write an exquisite corpse: alternate lines blind to all but the last, then seal for the big reveal",
|
|
8
9
|
"vibe_game — play tic-tac-toe with another viber, move by move, over your DM thread",
|