slashvibe-mcp 0.2.2 → 0.2.4

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 (162) hide show
  1. package/README.md +1 -0
  2. package/analytics.js +107 -0
  3. package/config.js +174 -3
  4. package/index.js +163 -34
  5. package/intelligence/index.js +45 -0
  6. package/intelligence/infer.js +316 -0
  7. package/intelligence/interests.js +369 -0
  8. package/intelligence/patterns.js +651 -0
  9. package/intelligence/proactive.js +358 -0
  10. package/intelligence/serendipity.js +306 -0
  11. package/notification-emitter.js +77 -0
  12. package/notify.js +141 -18
  13. package/package.json +14 -6
  14. package/presence.js +5 -1
  15. package/protocol/index.js +88 -1
  16. package/protocol/telegram-commands.js +199 -0
  17. package/store/api.js +469 -29
  18. package/store/index.js +7 -7
  19. package/store/local.js +67 -11
  20. package/store/profiles.js +435 -0
  21. package/store/reservations.js +321 -0
  22. package/store/skills.js +378 -0
  23. package/tools/_actions.js +491 -22
  24. package/tools/_connection-queue.js +257 -0
  25. package/tools/_discovery-enhanced.js +290 -0
  26. package/tools/_discovery.js +439 -0
  27. package/tools/_proactive-discovery.js +301 -0
  28. package/tools/_shared/index.js +64 -0
  29. package/tools/_work-context.js +338 -0
  30. package/tools/_work-context.manual-test.js +199 -0
  31. package/tools/_work-context.test.js +260 -0
  32. package/tools/admin-inbox.js +218 -0
  33. package/tools/agent-treasury.js +288 -0
  34. package/tools/agents.js +122 -0
  35. package/tools/analytics.js +191 -0
  36. package/tools/approve.js +197 -0
  37. package/tools/arcade.js +173 -0
  38. package/tools/artifact-create.js +247 -0
  39. package/tools/artifact-view.js +174 -0
  40. package/tools/artifacts-price.js +107 -0
  41. package/tools/ask-expert.js +160 -0
  42. package/tools/auto-suggest-connections.js +304 -0
  43. package/tools/away.js +68 -0
  44. package/tools/back.js +51 -0
  45. package/tools/become-expert.js +150 -0
  46. package/tools/bootstrap-skills.js +231 -0
  47. package/tools/bridge-dashboard.js +342 -0
  48. package/tools/bridge-health.js +400 -0
  49. package/tools/bridge-live.js +384 -0
  50. package/tools/bridges.js +383 -0
  51. package/tools/broadcast.js +286 -0
  52. package/tools/bye.js +4 -0
  53. package/tools/chat.js +202 -0
  54. package/tools/collaborative-drawing.js +286 -0
  55. package/tools/colorguess.js +281 -0
  56. package/tools/crossword.js +369 -0
  57. package/tools/discover-insights.js +379 -0
  58. package/tools/discover-momentum.js +256 -0
  59. package/tools/discover.js +675 -0
  60. package/tools/discovery-analytics.js +345 -0
  61. package/tools/discovery-auto-suggest.js +275 -0
  62. package/tools/discovery-bootstrap.js +267 -0
  63. package/tools/discovery-daily.js +375 -0
  64. package/tools/discovery-dashboard.js +385 -0
  65. package/tools/discovery-digest.js +314 -0
  66. package/tools/discovery-hub.js +357 -0
  67. package/tools/discovery-insights.js +384 -0
  68. package/tools/discovery-momentum.js +281 -0
  69. package/tools/discovery-monitor.js +319 -0
  70. package/tools/discovery-proactive.js +300 -0
  71. package/tools/dm.js +84 -14
  72. package/tools/draw.js +317 -0
  73. package/tools/drawing.js +310 -0
  74. package/tools/earnings.js +126 -0
  75. package/tools/echo.js +16 -0
  76. package/tools/farcaster.js +307 -0
  77. package/tools/feed.js +215 -0
  78. package/tools/follow.js +224 -0
  79. package/tools/friends.js +192 -0
  80. package/tools/game.js +218 -110
  81. package/tools/games-catalog.js +376 -0
  82. package/tools/games.js +313 -0
  83. package/tools/genesis.js +233 -0
  84. package/tools/gig-browse.js +206 -0
  85. package/tools/gig-complete.js +139 -0
  86. package/tools/guessnumber.js +194 -0
  87. package/tools/hangman.js +129 -0
  88. package/tools/help.js +269 -0
  89. package/tools/idea.js +217 -0
  90. package/tools/inbox.js +291 -25
  91. package/tools/init.js +657 -33
  92. package/tools/insights.js +123 -0
  93. package/tools/invite.js +142 -21
  94. package/tools/l2-bridge.js +272 -0
  95. package/tools/l2-status.js +217 -0
  96. package/tools/l2.js +206 -0
  97. package/tools/migrate.js +156 -0
  98. package/tools/mint.js +377 -0
  99. package/tools/multiplayer-game.js +275 -0
  100. package/tools/multiplayer-tictactoe.js +303 -0
  101. package/tools/mute.js +97 -0
  102. package/tools/notifications.js +415 -0
  103. package/tools/observe.js +200 -0
  104. package/tools/onboarding.js +147 -0
  105. package/tools/open.js +52 -3
  106. package/tools/party-game.js +314 -0
  107. package/tools/plan.js +225 -0
  108. package/tools/presence-agent.js +167 -0
  109. package/tools/profile.js +219 -0
  110. package/tools/proof-of-work.js +139 -0
  111. package/tools/pulse.js +218 -0
  112. package/tools/react.js +4 -0
  113. package/tools/release.js +83 -0
  114. package/tools/report.js +109 -0
  115. package/tools/reputation.js +175 -0
  116. package/tools/request.js +231 -0
  117. package/tools/reservations.js +116 -0
  118. package/tools/reserve.js +111 -0
  119. package/tools/riddle.js +240 -0
  120. package/tools/run-bootstrap.js +69 -0
  121. package/tools/schedule.js +367 -0
  122. package/tools/session.js +420 -0
  123. package/tools/session_price.js +128 -0
  124. package/tools/settings.js +200 -0
  125. package/tools/ship.js +188 -0
  126. package/tools/shipback.js +326 -0
  127. package/tools/skills-analytics.js +349 -0
  128. package/tools/skills-bootstrap.js +301 -0
  129. package/tools/skills-dashboard.js +268 -0
  130. package/tools/skills-exchange.js +342 -0
  131. package/tools/skills.js +380 -0
  132. package/tools/smart-intro.js +353 -0
  133. package/tools/social-inbox.js +326 -69
  134. package/tools/social-post.js +251 -66
  135. package/tools/social-processor.js +445 -0
  136. package/tools/solo-game.js +390 -0
  137. package/tools/start.js +296 -81
  138. package/tools/status.js +53 -6
  139. package/tools/storybuilder.js +331 -0
  140. package/tools/stuck.js +297 -0
  141. package/tools/subscribe.js +148 -0
  142. package/tools/subscriptions.js +134 -0
  143. package/tools/suggest-tags.js +184 -0
  144. package/tools/tag-suggestions.js +257 -0
  145. package/tools/telegram-bot.js +183 -0
  146. package/tools/telegram-setup.js +214 -0
  147. package/tools/tictactoe.js +155 -0
  148. package/tools/tip.js +120 -0
  149. package/tools/token.js +103 -0
  150. package/tools/twentyquestions.js +143 -0
  151. package/tools/update.js +1 -1
  152. package/tools/wallet.js +127 -0
  153. package/tools/watch.js +157 -0
  154. package/tools/webhook-test.js +388 -0
  155. package/tools/who.js +118 -25
  156. package/tools/withdraw.js +145 -0
  157. package/tools/wordassociation.js +247 -0
  158. package/tools/work-summary.js +96 -0
  159. package/tools/workshop-buddy.js +394 -0
  160. package/tools/workshop.js +327 -0
  161. package/version.json +12 -3
  162. package/tools/board.js +0 -130
package/README.md CHANGED
@@ -60,6 +60,7 @@ The MCP server connects to `slashvibe.dev` for:
60
60
 
61
61
  ## Related
62
62
 
63
+ - [GitHub](https://github.com/VibeCodingInc/vibe-mcp) - Source code
63
64
  - [slashvibe.dev](https://slashvibe.dev) - Web presence
64
65
  - [Spirit Protocol](https://spiritprotocol.io) - Parent ecosystem
65
66
  - [AIRC](https://airc.chat) - Agent identity protocol
package/analytics.js ADDED
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Analytics — Retention event tracking from MCP server
3
+ *
4
+ * Logs events to the /api/analytics/event endpoint for measuring
5
+ * user engagement and retention funnel performance.
6
+ *
7
+ * Usage:
8
+ * const analytics = require('./analytics');
9
+ * analytics.track('empty_inbox_action', { action: 'discover', source: 'inbox' });
10
+ */
11
+
12
+ const config = require('./config');
13
+
14
+ const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
15
+
16
+ /**
17
+ * Track an analytics event (fire and forget)
18
+ * @param {string} eventType - Event type (from valid types in api/lib/events.js)
19
+ * @param {object} data - Additional event data
20
+ */
21
+ async function track(eventType, data = {}) {
22
+ const handle = config.getHandle();
23
+ if (!handle) return; // Skip if not initialized
24
+
25
+ try {
26
+ // Fire and forget - don't await or block
27
+ fetch(`${API_URL}/api/analytics/events`, {
28
+ method: 'POST',
29
+ headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify({
31
+ type: eventType,
32
+ handle,
33
+ data: {
34
+ ...data,
35
+ client: 'mcp-server',
36
+ timestamp: Date.now()
37
+ }
38
+ })
39
+ }).catch(() => {}); // Silently ignore errors
40
+ } catch (e) {
41
+ // Analytics should never block or fail user flows
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Track empty inbox interaction
47
+ * @param {string} action - Which action was taken (or 'none' if user closed)
48
+ * @param {object} context - Context about the state (hadOnboardingTask, hadRecentShips, etc.)
49
+ */
50
+ function trackEmptyInbox(action, context = {}) {
51
+ // Track that user reached empty inbox state
52
+ track('empty_inbox_reached', {
53
+ hadRecentThreads: context.recentThreads?.length > 0,
54
+ hadOnboardingTask: !!context.onboardingTask,
55
+ hadRecentShips: context.recentShips?.length > 0
56
+ });
57
+
58
+ // If an action was taken, track it
59
+ if (action && action !== 'none') {
60
+ track('empty_inbox_action', {
61
+ action,
62
+ ...context
63
+ });
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Track lurk mode state change
69
+ * @param {boolean} enabled - Whether lurk mode was enabled or disabled
70
+ */
71
+ function trackLurkMode(enabled) {
72
+ track(enabled ? 'lurk_mode_enabled' : 'lurk_mode_disabled', {});
73
+ }
74
+
75
+ /**
76
+ * Track onboarding task completion
77
+ * @param {string} taskId - The task that was completed
78
+ */
79
+ function trackOnboardingTask(taskId) {
80
+ track('onboarding_task_completed', { taskId });
81
+ }
82
+
83
+ /**
84
+ * Track discovery initiation
85
+ * @param {string} source - Where discovery was initiated from (inbox, start, etc.)
86
+ */
87
+ function trackDiscovery(source) {
88
+ track('discovery_initiated', { source });
89
+ }
90
+
91
+ /**
92
+ * Track session lifecycle
93
+ * @param {string} event - 'started' or 'ended'
94
+ * @param {object} sessionData - Session metrics (duration, actions, etc.)
95
+ */
96
+ function trackSession(event, sessionData = {}) {
97
+ track(event === 'started' ? 'session_started' : 'session_ended', sessionData);
98
+ }
99
+
100
+ module.exports = {
101
+ track,
102
+ trackEmptyInbox,
103
+ trackLurkMode,
104
+ trackOnboardingTask,
105
+ trackDiscovery,
106
+ trackSession
107
+ };
package/config.js CHANGED
@@ -66,7 +66,15 @@ function save(config) {
66
66
  publicKey: config.publicKey || existing.publicKey || null,
67
67
  privateKey: config.privateKey || existing.privateKey || null,
68
68
  // Guided mode (AskUserQuestion menus)
69
- guided_mode: config.guided_mode !== undefined ? config.guided_mode : existing.guided_mode
69
+ guided_mode: config.guided_mode !== undefined ? config.guided_mode : existing.guided_mode,
70
+ // Notification level
71
+ notifications: config.notifications || existing.notifications || null,
72
+ // GitHub Activity settings
73
+ github_activity_enabled: config.github_activity_enabled !== undefined ? config.github_activity_enabled : existing.github_activity_enabled,
74
+ github_activity_privacy: config.github_activity_privacy || existing.github_activity_privacy || null,
75
+ // Privy OAuth token (persisted across MCP process restarts)
76
+ privyToken: config.privyToken || existing.privyToken || null,
77
+ authMethod: config.authMethod || existing.authMethod || null
70
78
  };
71
79
  fs.writeFileSync(PRIMARY_CONFIG, JSON.stringify(data, null, 2));
72
80
  }
@@ -203,8 +211,80 @@ function setAuthToken(token, sessionId = null) {
203
211
  }
204
212
 
205
213
  function getAuthToken() {
214
+ // First check session data
215
+ const data = getSessionData();
216
+ if (data?.token) return data.token;
217
+
218
+ // Fall back to shared config (persisted across MCP process restarts)
219
+ const cfg = load();
220
+ return cfg?.privyToken || null;
221
+ }
222
+
223
+ /**
224
+ * Save Privy JWT token (used after browser OAuth flow)
225
+ * @param {string} token - Privy access token
226
+ */
227
+ function savePrivyToken(token) {
228
+ // Save to session data
229
+ const data = getSessionData() || {};
230
+ saveSessionData({
231
+ ...data,
232
+ sessionId: data.sessionId || generateSessionId(),
233
+ token,
234
+ authMethod: 'privy' // Track that this is a Privy token
235
+ });
236
+
237
+ // Also save to shared config for persistence across MCP restarts
238
+ const cfg = load();
239
+ cfg.privyToken = token;
240
+ cfg.authMethod = 'privy';
241
+ save(cfg);
242
+ }
243
+
244
+ /**
245
+ * Check if user has Privy auth (vs legacy keypair)
246
+ * Accepts both 'privy' and 'browser' as valid auth methods
247
+ */
248
+ function hasPrivyAuth() {
249
+ const validAuthMethods = ['privy', 'browser'];
250
+
206
251
  const data = getSessionData();
207
- return data?.token || null;
252
+ if (validAuthMethods.includes(data?.authMethod) && data?.token) return true;
253
+
254
+ const cfg = load();
255
+ return validAuthMethods.includes(cfg?.authMethod) && cfg?.privyToken;
256
+ }
257
+
258
+ /**
259
+ * Remove keypair after migration to Privy
260
+ * Clears private key from config (security improvement)
261
+ */
262
+ function removeKeypair() {
263
+ const cfg = load();
264
+ delete cfg.publicKey;
265
+ delete cfg.privateKey;
266
+ save(cfg);
267
+
268
+ // Also clear from session
269
+ const data = getSessionData();
270
+ if (data) {
271
+ delete data.publicKey;
272
+ delete data.privateKey;
273
+ saveSessionData(data);
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Get auth URL for browser-based GitHub OAuth
279
+ * @param {string|null} handle - Custom handle (optional - defaults to GitHub username)
280
+ */
281
+ function getAuthUrl(handle = null) {
282
+ const apiUrl = getApiUrl();
283
+ if (handle) {
284
+ return `${apiUrl}/api/auth/github?handle=${encodeURIComponent(handle)}`;
285
+ }
286
+ // No handle = use GitHub username as handle
287
+ return `${apiUrl}/api/auth/github`;
208
288
  }
209
289
 
210
290
  function clearSession() {
@@ -228,6 +308,81 @@ function setGuidedMode(enabled) {
228
308
  save(config);
229
309
  }
230
310
 
311
+ // Notification settings
312
+ // Levels: "all" | "mentions" | "off"
313
+ // - all: desktop + bell for unread, mentions, presence (default)
314
+ // - mentions: only @mentions trigger notifications
315
+ // - off: no notifications
316
+ function getNotifications() {
317
+ const config = load();
318
+ return config.notifications || 'all';
319
+ }
320
+
321
+ function setNotifications(level) {
322
+ const validLevels = ['all', 'mentions', 'off'];
323
+ if (!validLevels.includes(level)) {
324
+ throw new Error(`Invalid notification level. Use: ${validLevels.join(', ')}`);
325
+ }
326
+ const config = load();
327
+ config.notifications = level;
328
+ save(config);
329
+ }
330
+
331
+ // GitHub Activity settings
332
+ // Shows shipping status based on GitHub commit activity
333
+ // Default: false (opt-in for privacy)
334
+ function getGithubActivityEnabled() {
335
+ const config = load();
336
+ return config.github_activity_enabled === true;
337
+ }
338
+
339
+ function setGithubActivityEnabled(enabled) {
340
+ const config = load();
341
+ config.github_activity_enabled = enabled;
342
+ save(config);
343
+ }
344
+
345
+ // GitHub Activity privacy level
346
+ // Levels: "full" | "status_only" | "off"
347
+ // - full: Show repos, commit counts, tech stack (default when enabled)
348
+ // - status_only: Just show shipping badge (🔥/⚡), no details
349
+ // - off: Disabled completely
350
+ function getGithubActivityPrivacy() {
351
+ const config = load();
352
+ return config.github_activity_privacy || 'full';
353
+ }
354
+
355
+ function setGithubActivityPrivacy(level) {
356
+ const validLevels = ['full', 'status_only', 'off'];
357
+ if (!validLevels.includes(level)) {
358
+ throw new Error(`Invalid privacy level. Use: ${validLevels.join(', ')}`);
359
+ }
360
+ const config = load();
361
+ config.github_activity_privacy = level;
362
+ save(config);
363
+ }
364
+
365
+ // API URL — central endpoint for all API calls
366
+ function getApiUrl() {
367
+ return process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
368
+ }
369
+
370
+ // ─────────────────────────────────────────────────────────────
371
+ // Generic key-value store for ephemeral session state
372
+ // Used by presence-agent, mute, and other tools for runtime state
373
+ // NOT persisted to disk — resets when MCP server restarts
374
+ // ─────────────────────────────────────────────────────────────
375
+ const sessionState = {};
376
+
377
+ function get(key, defaultValue = null) {
378
+ return sessionState[key] !== undefined ? sessionState[key] : defaultValue;
379
+ }
380
+
381
+ function set(key, value) {
382
+ sessionState[key] = value;
383
+ return value;
384
+ }
385
+
231
386
  module.exports = {
232
387
  VIBE_DIR,
233
388
  CONFIG_FILE,
@@ -248,5 +403,21 @@ module.exports = {
248
403
  clearSession,
249
404
  generateSessionId,
250
405
  getGuidedMode,
251
- setGuidedMode
406
+ setGuidedMode,
407
+ getNotifications,
408
+ setNotifications,
409
+ // GitHub Activity settings
410
+ getGithubActivityEnabled,
411
+ setGithubActivityEnabled,
412
+ getGithubActivityPrivacy,
413
+ setGithubActivityPrivacy,
414
+ getApiUrl,
415
+ // Privy OAuth helpers
416
+ savePrivyToken,
417
+ hasPrivyAuth,
418
+ removeKeypair,
419
+ getAuthUrl,
420
+ // Generic key-value for ephemeral session state
421
+ get,
422
+ set
252
423
  };
package/index.js CHANGED
@@ -10,9 +10,11 @@ const presence = require('./presence');
10
10
  const config = require('./config');
11
11
  const store = require('./store');
12
12
  const prompts = require('./prompts');
13
+ const NotificationEmitter = require('./notification-emitter');
14
+ const authStore = require('./auth-store');
13
15
 
14
16
  // Tools that shouldn't show presence footer (would be redundant/noisy)
15
- const SKIP_FOOTER_TOOLS = ['vibe_init', 'vibe_doctor', 'vibe_test', 'vibe_update'];
17
+ const SKIP_FOOTER_TOOLS = ['vibe_init', 'vibe_doctor', 'vibe_test', 'vibe_update', 'vibe_settings', 'vibe_notifications'];
16
18
 
17
19
  // Infer user prompt from tool arguments (for pattern logging)
18
20
  function inferPromptFromArgs(toolName, args) {
@@ -35,15 +37,30 @@ function inferPromptFromArgs(toolName, args) {
35
37
  case 'context': return 'share context';
36
38
  case 'summarize': return 'summarize session';
37
39
  case 'bye': return 'end session';
38
- case 'remember': return `remember about ${handle}`;
39
- case 'recall': return `recall ${handle}`;
40
- case 'forget': return `forget ${handle}`;
41
40
  case 'board': return args.content ? 'post to board' : 'view board';
41
+ case 'observe': return args.content ? 'record observation' : 'view observations';
42
42
  case 'invite': return 'generate invite';
43
43
  case 'echo': return 'send feedback';
44
44
  case 'x_mentions': return 'check x mentions';
45
45
  case 'x_reply': return 'reply on x';
46
46
  case 'handoff': return `handoff task to ${handle}`;
47
+ case 'reserve': return args.paths ? `reserve ${args.paths.join(', ')}` : 'reserve files';
48
+ case 'release': return `release ${args.reservation_id || 'reservation'}`;
49
+ case 'reservations': return 'list reservations';
50
+ case 'solo_game': return `play ${args.game || 'game'}`;
51
+ case 'game': return `play ${args.game || 'game'} with ${handle}`;
52
+ case 'multiplayer_game': return `multiplayer ${args.game || 'game'}`;
53
+ case 'drawing': return args.action ? `drawing ${args.action}` : 'collaborative drawing';
54
+ case 'crossword': return `crossword ${args.action || 'daily'}`;
55
+ case 'discover': return `discover ${args.command || 'suggest'}`;
56
+ case 'suggest_tags': return `suggest tags ${args.command || 'suggest'}`;
57
+ case 'settings': return args.mute ? `mute for ${args.mute}` : 'view settings';
58
+ case 'create_artifact': return `create ${args.template || 'artifact'}: ${args.title || 'untitled'}`;
59
+ case 'view_artifact': return args.slug ? `view artifact ${args.slug}` : `list ${args.list || 'artifacts'}`;
60
+ case 'broadcast': return args.action ? `broadcast ${args.action}` : 'broadcast status';
61
+ case 'watch': return args.target ? `watch ${args.target}` : 'list live broadcasts';
62
+ case 'session': return args.action ? `session ${args.action}` : 'list sessions';
63
+ case 'chat': return args.message ? `chat "${args.message.slice(0, 30)}..."` : 'view chat';
47
64
  default: return `${action} ${handle}`.trim() || null;
48
65
  }
49
66
  }
@@ -145,12 +162,14 @@ async function getPresenceFooter() {
145
162
  }
146
163
  }
147
164
 
148
- // Load all tools
149
- const tools = {
165
+ // Load core tools (always available)
166
+ const coreTools = {
150
167
  // Entry point
151
168
  vibe_start: require('./tools/start'),
152
169
  // Core
153
170
  vibe_init: require('./tools/init'),
171
+ vibe_token: require('./tools/token'), // Privy OAuth token setter
172
+ vibe_migrate: require('./tools/migrate'), // Migrate existing handle to GitHub auth
154
173
  vibe_who: require('./tools/who'),
155
174
  vibe_ping: require('./tools/ping'),
156
175
  vibe_react: require('./tools/react'),
@@ -159,25 +178,56 @@ const tools = {
159
178
  vibe_open: require('./tools/open'),
160
179
  vibe_status: require('./tools/status'),
161
180
  vibe_context: require('./tools/context'),
181
+ vibe_work_summary: require('./tools/work-summary'), // Ambient context for composing messages
162
182
  vibe_summarize: require('./tools/summarize'),
163
183
  vibe_bye: require('./tools/bye'),
164
184
  vibe_game: require('./tools/game'),
185
+ vibe_solo_game: require('./tools/solo-game'),
186
+ vibe_party_game: require('./tools/party-game'),
187
+ vibe_multiplayer_game: require('./tools/multiplayer-game'),
188
+ vibe_drawing: require('./tools/drawing'),
189
+ vibe_crossword: require('./tools/crossword'),
165
190
  // AIRC Handoff (v1) — context portability
166
191
  vibe_handoff: require('./tools/handoff'),
167
- // Memory tools (Tier 1 — Collaborative Memory)
168
- vibe_remember: require('./tools/remember'),
169
- vibe_recall: require('./tools/recall'),
170
- vibe_forget: require('./tools/forget'),
192
+ // File reservations (advisory locks)
193
+ vibe_reserve: require('./tools/reserve'),
194
+ vibe_release: require('./tools/release'),
195
+ vibe_reservations: require('./tools/reservations'),
196
+ // Memory tools removed — simplified UX
171
197
  // Consent (AIRC compliance)
172
198
  vibe_consent: require('./tools/consent'),
199
+ // Trust & Safety
200
+ vibe_report: require('./tools/report'),
201
+ // Support
202
+ vibe_help: require('./tools/help'),
203
+ vibe_agents: require('./tools/agents'),
173
204
  // Community
174
205
  vibe_invite: require('./tools/invite'),
175
- vibe_board: require('./tools/board'),
176
206
  vibe_submit: require('./tools/submit'),
177
- // Diagnostics
178
- vibe_test: require('./tools/test'),
179
- vibe_doctor: require('./tools/doctor'),
180
- vibe_update: require('./tools/update'),
207
+ vibe_observe: require('./tools/observe'),
208
+ // Creative Layer — Ideas, Requests, Ships
209
+ vibe_idea: require('./tools/idea'),
210
+ vibe_request: require('./tools/request'),
211
+ vibe_ship: require('./tools/ship'),
212
+ vibe_feed: require('./tools/feed'),
213
+ vibe_insights: require('./tools/insights'),
214
+ // Launch Analytics — Real-time metrics dashboard
215
+ vibe_analytics: require('./tools/analytics'),
216
+ // Artifacts — Just-in-time social objects
217
+ vibe_create_artifact: require('./tools/artifact-create'),
218
+ vibe_view_artifact: require('./tools/artifact-view'),
219
+ // Plans — Async approval workflow
220
+ vibe_plan: require('./tools/plan'),
221
+ vibe_approve: require('./tools/approve'),
222
+ // Discovery & Matchmaking
223
+ vibe_discover: require('./tools/discover'),
224
+ vibe_suggest_tags: require('./tools/suggest-tags'),
225
+ // Follow system
226
+ vibe_follow: require('./tools/follow'),
227
+ // GitHub friends import (Plaxo move)
228
+ vibe_friends: require('./tools/friends'),
229
+ // Scheduling
230
+ vibe_schedule: require('./tools/schedule'),
181
231
  // @echo feedback agent (by Flynn)
182
232
  vibe_echo: require('./tools/echo'),
183
233
  // X/Twitter bridge
@@ -186,19 +236,83 @@ const tools = {
186
236
  // Unified social inbox (Phase 1a)
187
237
  vibe_social_inbox: require('./tools/social-inbox'),
188
238
  vibe_social_post: require('./tools/social-post'),
189
- // Language evolution
190
- vibe_patterns: require('./tools/patterns')
239
+ // Settings
240
+ vibe_settings: require('./tools/settings'),
241
+ // External notification channels
242
+ vibe_notifications: require('./tools/notifications'),
243
+ // Background presence agent (Claude Code 2.1)
244
+ vibe_presence_agent: require('./tools/presence-agent'),
245
+ // Onboarding checklist
246
+ vibe_onboarding: require('./tools/onboarding'),
247
+ // VIBE L2 tools
248
+ vibe_l2: require('./tools/l2'),
249
+ vibe_l2_status: require('./tools/l2-status'),
250
+ vibe_shipback: require('./tools/shipback'),
251
+ vibe_bridge: require('./tools/l2-bridge'),
252
+ // Gig Marketplace & Proof-of-Work
253
+ vibe_gig_browse: require('./tools/gig-browse'),
254
+ vibe_gig_complete: require('./tools/gig-complete'),
255
+ vibe_proof_of_work: require('./tools/proof-of-work'),
256
+ vibe_artifacts_price: require('./tools/artifacts-price'),
257
+ // Watch Me Code — Live broadcasting & sessions
258
+ vibe_broadcast: require('./tools/broadcast'),
259
+ vibe_watch: require('./tools/watch'),
260
+ vibe_session: require('./tools/session'),
261
+ vibe_chat: require('./tools/chat'),
262
+ // Help Signal — Symbiosis layer
263
+ vibe_stuck: require('./tools/stuck'),
264
+ // VIBE Economy — Earnings, Withdrawals, Subscriptions
265
+ vibe_earnings: require('./tools/earnings'),
266
+ vibe_withdraw: require('./tools/withdraw'),
267
+ vibe_session_price: require('./tools/session_price'),
268
+ vibe_subscribe: require('./tools/subscribe'),
269
+ vibe_subscriptions: require('./tools/subscriptions')
191
270
  };
192
271
 
272
+ // Admin tools (only loaded when VIBE_ADMIN=true)
273
+ const adminTools = process.env.VIBE_ADMIN === 'true' ? {
274
+ vibe_admin_inbox: require('./tools/admin-inbox'),
275
+ vibe_test: require('./tools/test'),
276
+ vibe_doctor: require('./tools/doctor'),
277
+ vibe_update: require('./tools/update'),
278
+ vibe_patterns: require('./tools/patterns'),
279
+ } : {};
280
+
281
+ // Combine tools
282
+ const tools = { ...coreTools, ...adminTools };
283
+
193
284
  /**
194
285
  * MCP Protocol Handler
195
286
  */
196
287
  class VibeMCPServer {
197
288
  constructor() {
289
+ // Hydrate auth state from disk FIRST (before any tools need it)
290
+ authStore.hydrate();
291
+
292
+ // Initialize notification emitter
293
+ this.notifier = new NotificationEmitter(this);
294
+
295
+ // Make notifier globally accessible for tools and store layer
296
+ global.vibeNotifier = this.notifier;
297
+
198
298
  // Start presence heartbeat
199
299
  presence.start();
200
300
  }
201
301
 
302
+ /**
303
+ * Send MCP notification
304
+ * Called by NotificationEmitter to push list_changed events
305
+ */
306
+ notification(payload) {
307
+ // Send notification via stdout (MCP protocol)
308
+ const notification = {
309
+ jsonrpc: '2.0',
310
+ method: payload.method,
311
+ params: payload.params || {}
312
+ };
313
+ process.stdout.write(JSON.stringify(notification) + '\n');
314
+ }
315
+
202
316
  async handleRequest(request) {
203
317
  const { method, params, id } = request;
204
318
 
@@ -252,34 +366,32 @@ class VibeMCPServer {
252
366
 
253
367
  const result = await tool.handler(args);
254
368
 
369
+ // Emit list_changed notification for state-changing tools
370
+ // This triggers Claude to refresh without reconnection
371
+ const stateChangingTools = [
372
+ 'vibe_dm', 'vibe_ping', 'vibe_react',
373
+ 'vibe_status', 'vibe_context', 'vibe_handoff',
374
+ 'vibe_reserve', 'vibe_release',
375
+ 'vibe_broadcast', 'vibe_session', 'vibe_chat'
376
+ ];
377
+ if (stateChangingTools.includes(params.name)) {
378
+ // Debounced notification (prevents spam)
379
+ global.vibeNotifier?.emitChange(params.name);
380
+ }
381
+
255
382
  // Add ambient presence footer (unless tool is in skip list)
256
383
  let footer = '';
257
384
  if (!SKIP_FOOTER_TOOLS.includes(params.name)) {
258
385
  footer = await getPresenceFooter();
259
386
  }
260
387
 
261
- // Build hint indicator for Claude to trigger dashboard flows
262
- let hintIndicator = '';
263
- if (result.hint || result.actions) {
264
- const hintData = {
265
- ...(result.hint && { hint: result.hint }),
266
- ...(result.suggestion && { suggestion: result.suggestion }),
267
- ...(result.unread_count && { unread_count: result.unread_count }),
268
- ...(result.for_handle && { for_handle: result.for_handle }),
269
- ...(result.memories && { memories: result.memories }),
270
- ...(result.threads && { threads: result.threads }),
271
- ...(result.actions && { actions: result.actions })
272
- };
273
- hintIndicator = `\n\n<!-- vibe:dashboard ${JSON.stringify(hintData)} -->`;
274
- }
275
-
276
388
  return {
277
389
  jsonrpc: '2.0',
278
390
  id,
279
391
  result: {
280
392
  content: [{
281
393
  type: 'text',
282
- text: (result.display || JSON.stringify(result, null, 2)) + hintIndicator + footer
394
+ text: (result.display || JSON.stringify(result, null, 2)) + footer
283
395
  }]
284
396
  }
285
397
  };
@@ -333,9 +445,26 @@ class VibeMCPServer {
333
445
  process.stderr.write('vibe init → set identity\n');
334
446
  process.stderr.write('vibe who → see who\'s around\n');
335
447
  process.stderr.write('vibe dm → send a message\n\n');
448
+
449
+ // Check for updates (non-blocking)
450
+ this.checkForUpdates();
451
+ }
452
+
453
+ async checkForUpdates() {
454
+ try {
455
+ const { checkForUpdates, formatUpdateNotification } = await import('./auto-update.js');
456
+ const update = await checkForUpdates();
457
+
458
+ if (update) {
459
+ const notification = formatUpdateNotification(update);
460
+ process.stderr.write(notification);
461
+ }
462
+ } catch (error) {
463
+ // Silent fail - don't block startup
464
+ }
336
465
  }
337
466
  }
338
467
 
339
468
  // Start
340
469
  const server = new VibeMCPServer();
341
- server.start();
470
+ server.start();
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Intelligence Module — Ambient Social Awareness
3
+ *
4
+ * Five layers of intelligence:
5
+ * 1. Infer — Smart state detection from context signals
6
+ * 2. Serendipity — Surface meaningful coincidences
7
+ * 3. Proactive — Background agent for social moments
8
+ * 4. Patterns — Persistent behavioral memory (work, social, creative)
9
+ * 5. Interests — Live interest inference from context (Phase 2)
10
+ */
11
+
12
+ const infer = require('./infer');
13
+ const serendipity = require('./serendipity');
14
+ const proactive = require('./proactive');
15
+ const patterns = require('./patterns');
16
+ const interests = require('./interests');
17
+
18
+ module.exports = {
19
+ // Inference
20
+ inferState: infer.inferState,
21
+ enhanceUserWithInference: infer.enhanceUserWithInference,
22
+ enhanceUsersWithInference: infer.enhanceUsersWithInference,
23
+ STATES: infer.STATES,
24
+
25
+ // Serendipity
26
+ findSerendipity: serendipity.findSerendipity,
27
+ getTopSerendipity: serendipity.getTopSerendipity,
28
+ getAllSerendipity: serendipity.getAllSerendipity,
29
+
30
+ // Proactive
31
+ generateProactiveSuggestions: proactive.generateProactiveSuggestions,
32
+ getProactiveSummary: proactive.getProactiveSummary,
33
+ checkProactiveOpportunities: proactive.checkProactiveOpportunities,
34
+ markAway: proactive.markAway,
35
+ markBack: proactive.markBack,
36
+ setSessionStart: proactive.setSessionStart,
37
+
38
+ // Patterns (persistent behavioral memory)
39
+ patterns,
40
+
41
+ // Interests (live context inference - Phase 2)
42
+ inferLiveInterests: interests.inferLiveInterests,
43
+ mergeInterests: interests.mergeInterests,
44
+ formatLiveInterests: interests.formatLiveInterests
45
+ };