slashvibe-mcp 0.2.9 → 0.3.13

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 (205) hide show
  1. package/README.md +1 -0
  2. package/analytics.js +107 -0
  3. package/auth-store.js +148 -0
  4. package/auto-update.js +130 -0
  5. package/bridges/bridge-monitor.js +388 -0
  6. package/bridges/discord-bot.js +431 -0
  7. package/bridges/farcaster.js +299 -0
  8. package/bridges/telegram.js +261 -0
  9. package/bridges/webhook-health.js +420 -0
  10. package/bridges/webhook-server.js +437 -0
  11. package/bridges/whatsapp.js +441 -0
  12. package/bridges/x-webhook.js +423 -0
  13. package/config.js +154 -5
  14. package/crypto.js +3 -8
  15. package/games/arcade.js +406 -0
  16. package/games/chess.js +451 -0
  17. package/games/colorguess.js +343 -0
  18. package/games/crossword-words.js +171 -0
  19. package/games/crossword.js +461 -0
  20. package/games/drawing.js +347 -0
  21. package/games/gameroulette.js +300 -0
  22. package/games/gamerouter.js +336 -0
  23. package/games/gamestatus.js +337 -0
  24. package/games/guessnumber.js +209 -0
  25. package/games/hangman.js +279 -0
  26. package/games/memory.js +338 -0
  27. package/games/multiplayer-tictactoe.js +389 -0
  28. package/games/pixelart.js +399 -0
  29. package/games/quickduel.js +354 -0
  30. package/games/riddle.js +371 -0
  31. package/games/rockpaperscissors.js +291 -0
  32. package/games/snake.js +406 -0
  33. package/games/storybuilder.js +343 -0
  34. package/games/tictactoe.js +345 -0
  35. package/games/twentyquestions.js +286 -0
  36. package/games/twotruths.js +207 -0
  37. package/games/werewolf.js +508 -0
  38. package/games/wordassociation.js +247 -0
  39. package/games/wordchain.js +135 -0
  40. package/index.js +105 -216
  41. package/intelligence/index.js +45 -0
  42. package/intelligence/infer.js +316 -0
  43. package/intelligence/interests.js +369 -0
  44. package/intelligence/patterns.js +651 -0
  45. package/intelligence/proactive.js +358 -0
  46. package/intelligence/serendipity.js +306 -0
  47. package/memory.js +166 -0
  48. package/notification-emitter.js +77 -0
  49. package/notify.js +102 -1
  50. package/package.json +21 -7
  51. package/prompts.js +1 -1
  52. package/protocol/index.js +161 -1
  53. package/setup.js +402 -0
  54. package/store/api.js +528 -82
  55. package/store/profiles.js +160 -12
  56. package/tools/_actions.js +463 -16
  57. package/tools/_deprecated/auto-suggest-connections.js +304 -0
  58. package/tools/_deprecated/bootstrap-skills.js +231 -0
  59. package/tools/_deprecated/bridge-dashboard.js +342 -0
  60. package/tools/_deprecated/bridge-health.js +400 -0
  61. package/tools/_deprecated/bridge-live.js +384 -0
  62. package/tools/_deprecated/bridges.js +383 -0
  63. package/tools/_deprecated/colorguess.js +281 -0
  64. package/tools/_deprecated/discover-insights.js +379 -0
  65. package/tools/_deprecated/discover-momentum.js +256 -0
  66. package/tools/_deprecated/discovery-analytics.js +345 -0
  67. package/tools/_deprecated/discovery-auto-suggest.js +275 -0
  68. package/tools/_deprecated/discovery-bootstrap.js +267 -0
  69. package/tools/_deprecated/discovery-daily.js +375 -0
  70. package/tools/_deprecated/discovery-dashboard.js +385 -0
  71. package/tools/_deprecated/discovery-digest.js +314 -0
  72. package/tools/_deprecated/discovery-hub.js +357 -0
  73. package/tools/_deprecated/discovery-insights.js +384 -0
  74. package/tools/_deprecated/discovery-momentum.js +281 -0
  75. package/tools/_deprecated/discovery-monitor.js +319 -0
  76. package/tools/_deprecated/discovery-proactive.js +300 -0
  77. package/tools/_deprecated/draw.js +317 -0
  78. package/tools/_deprecated/farcaster.js +307 -0
  79. package/tools/_deprecated/forget.js +119 -0
  80. package/tools/_deprecated/games-catalog.js +376 -0
  81. package/tools/_deprecated/games.js +313 -0
  82. package/tools/_deprecated/guessnumber.js +194 -0
  83. package/tools/_deprecated/hangman.js +129 -0
  84. package/tools/_deprecated/multiplayer-tictactoe.js +303 -0
  85. package/tools/_deprecated/recall.js +147 -0
  86. package/tools/_deprecated/remember.js +86 -0
  87. package/tools/_deprecated/riddle.js +240 -0
  88. package/tools/_deprecated/run-bootstrap.js +69 -0
  89. package/tools/_deprecated/skills-analytics.js +349 -0
  90. package/tools/_deprecated/skills-bootstrap.js +301 -0
  91. package/tools/_deprecated/skills-dashboard.js +268 -0
  92. package/tools/_deprecated/skills.js +380 -0
  93. package/tools/_deprecated/smart-intro.js +353 -0
  94. package/tools/_deprecated/storybuilder.js +331 -0
  95. package/tools/_deprecated/telegram-bot.js +183 -0
  96. package/tools/_deprecated/telegram-setup.js +214 -0
  97. package/tools/_deprecated/twentyquestions.js +143 -0
  98. package/tools/_discovery-enhanced.js +290 -0
  99. package/tools/_discovery.js +439 -0
  100. package/tools/_proactive-discovery.js +301 -0
  101. package/tools/_shared/index.js +64 -0
  102. package/tools/_shared.js +234 -0
  103. package/tools/_work-context.js +338 -0
  104. package/tools/_work-context.manual-test.js +199 -0
  105. package/tools/_work-context.test.js +260 -0
  106. package/tools/activity.js +220 -0
  107. package/tools/admin-inbox.js +218 -0
  108. package/tools/agent-treasury.js +288 -0
  109. package/tools/analytics.js +191 -0
  110. package/tools/approve.js +197 -0
  111. package/tools/arcade.js +173 -0
  112. package/tools/artifact-create.js +17 -11
  113. package/tools/artifact-view.js +31 -1
  114. package/tools/artifacts-price.js +47 -43
  115. package/tools/ask-expert.js +160 -0
  116. package/tools/available.js +120 -0
  117. package/tools/become-expert.js +150 -0
  118. package/tools/broadcast.js +286 -0
  119. package/tools/chat.js +202 -0
  120. package/tools/collaborative-drawing.js +286 -0
  121. package/tools/connection-status.js +178 -0
  122. package/tools/crossword.js +17 -1
  123. package/tools/discover.js +350 -34
  124. package/tools/dm.js +115 -73
  125. package/tools/drawing.js +1 -1
  126. package/tools/earnings.js +126 -0
  127. package/tools/echo.js +16 -0
  128. package/tools/feed.js +51 -7
  129. package/tools/follow.js +224 -0
  130. package/tools/friends.js +207 -0
  131. package/tools/game.js +2 -2
  132. package/tools/genesis.js +233 -0
  133. package/tools/gig-browse.js +206 -0
  134. package/tools/gig-complete.js +144 -0
  135. package/tools/handoff.js +7 -1
  136. package/tools/help.js +4 -4
  137. package/tools/idea.js +9 -2
  138. package/tools/inbox.js +334 -28
  139. package/tools/init.js +727 -36
  140. package/tools/invite.js +15 -4
  141. package/tools/l2-bridge.js +272 -0
  142. package/tools/l2-status.js +217 -0
  143. package/tools/l2.js +206 -0
  144. package/tools/migrate.js +156 -0
  145. package/tools/mint.js +377 -0
  146. package/tools/multiplayer-game.js +1 -1
  147. package/tools/notifications.js +415 -0
  148. package/tools/observe.js +200 -0
  149. package/tools/onboarding.js +147 -0
  150. package/tools/open.js +143 -12
  151. package/tools/party-game.js +2 -2
  152. package/tools/plan.js +225 -0
  153. package/tools/presence-agent.js +7 -0
  154. package/tools/proof-of-work.js +100 -104
  155. package/tools/pulse.js +218 -0
  156. package/tools/reply.js +166 -0
  157. package/tools/report.js +2 -2
  158. package/tools/reputation.js +175 -0
  159. package/tools/request.js +17 -3
  160. package/tools/schedule.js +367 -0
  161. package/tools/search-messages.js +123 -0
  162. package/tools/session.js +420 -0
  163. package/tools/session_price.js +128 -0
  164. package/tools/settings.js +126 -3
  165. package/tools/ship.js +31 -8
  166. package/tools/shipback.js +326 -0
  167. package/tools/smart-check.js +201 -0
  168. package/tools/social-processor.js +445 -0
  169. package/tools/solo-game.js +1 -1
  170. package/tools/start.js +335 -93
  171. package/tools/status.js +53 -6
  172. package/tools/stuck.js +297 -0
  173. package/tools/subscribe.js +148 -0
  174. package/tools/subscriptions.js +134 -0
  175. package/tools/suggest-tags.js +6 -8
  176. package/tools/tag-suggestions.js +257 -0
  177. package/tools/tip.js +193 -0
  178. package/tools/token.js +103 -0
  179. package/tools/update.js +1 -1
  180. package/tools/wallet.js +239 -186
  181. package/tools/watch.js +157 -0
  182. package/tools/webhook-test.js +388 -0
  183. package/tools/who.js +54 -3
  184. package/tools/withdraw.js +145 -0
  185. package/tools/work-summary.js +96 -0
  186. package/tools/workshop.js +327 -0
  187. package/tools/x-mentions.js +1 -1
  188. package/version.json +14 -3
  189. package/tools/artifacts-buy.js +0 -111
  190. package/tools/connect.js +0 -284
  191. package/tools/gigs-apply.js +0 -99
  192. package/tools/gigs-browse.js +0 -114
  193. package/tools/gigs-complete.js +0 -128
  194. package/tools/gigs-post.js +0 -140
  195. package/tools/live-off.js +0 -109
  196. package/tools/live-watch.js +0 -176
  197. package/tools/live.js +0 -128
  198. package/tools/sessions-browse.js +0 -105
  199. package/tools/whats-happening.js +0 -125
  200. /package/tools/{away.js → _deprecated/away.js} +0 -0
  201. /package/tools/{back.js → _deprecated/back.js} +0 -0
  202. /package/tools/{mute.js → _deprecated/mute.js} +0 -0
  203. /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
  204. /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
  205. /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
package/memory.js ADDED
@@ -0,0 +1,166 @@
1
+ /**
2
+ * /vibe Memory — Local, thread-scoped, append-only
3
+ *
4
+ * "Messages may contain meaning. Memory requires consent."
5
+ *
6
+ * Rules:
7
+ * - Local-first: User can inspect ~/.vibe/memory/
8
+ * - Explicit consent: No ambient surveillance
9
+ * - Thread-scoped: Memories don't leak across contexts
10
+ * - Inspectable: Plain JSONL, no encryption
11
+ * - Append-only: Audit trail, no silent edits
12
+ */
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const config = require('./config');
17
+
18
+ const MEMORY_DIR = path.join(config.VIBE_DIR, 'memory');
19
+
20
+ function ensureMemoryDir() {
21
+ if (!fs.existsSync(MEMORY_DIR)) {
22
+ fs.mkdirSync(MEMORY_DIR, { recursive: true });
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Get the thread file path for a handle
28
+ * Thread files are named: thread_HANDLE.jsonl
29
+ */
30
+ function getThreadFile(handle) {
31
+ ensureMemoryDir();
32
+ // Sanitize handle for filename
33
+ const safeHandle = handle.replace(/[^a-zA-Z0-9_-]/g, '_').toLowerCase();
34
+ return path.join(MEMORY_DIR, `thread_${safeHandle}.jsonl`);
35
+ }
36
+
37
+ /**
38
+ * Append a memory to a thread
39
+ * Returns the memory object with timestamp
40
+ */
41
+ function remember(handle, observation, metadata = {}) {
42
+ const threadFile = getThreadFile(handle);
43
+ const myHandle = config.getHandle();
44
+
45
+ const memory = {
46
+ id: Date.now().toString(36) + Math.random().toString(36).substring(2, 8),
47
+ timestamp: new Date().toISOString(),
48
+ observation,
49
+ from: myHandle,
50
+ about: handle,
51
+ ...metadata
52
+ };
53
+
54
+ // Append to JSONL file (one JSON object per line)
55
+ const line = JSON.stringify(memory) + '\n';
56
+ fs.appendFileSync(threadFile, line);
57
+
58
+ return memory;
59
+ }
60
+
61
+ /**
62
+ * Recall memories from a thread
63
+ * Returns array of memory objects, newest first
64
+ */
65
+ function recall(handle, limit = 10) {
66
+ const threadFile = getThreadFile(handle);
67
+
68
+ if (!fs.existsSync(threadFile)) {
69
+ return [];
70
+ }
71
+
72
+ try {
73
+ const content = fs.readFileSync(threadFile, 'utf8');
74
+ const lines = content.trim().split('\n').filter(l => l.trim());
75
+ const memories = lines.map(line => {
76
+ try {
77
+ return JSON.parse(line);
78
+ } catch (e) {
79
+ return null;
80
+ }
81
+ }).filter(m => m !== null);
82
+
83
+ // Return newest first, limited
84
+ return memories.reverse().slice(0, limit);
85
+ } catch (e) {
86
+ return [];
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Count memories in a thread
92
+ */
93
+ function count(handle) {
94
+ const memories = recall(handle, 10000);
95
+ return memories.length;
96
+ }
97
+
98
+ /**
99
+ * Forget all memories for a thread
100
+ * Returns number of memories deleted
101
+ */
102
+ function forget(handle) {
103
+ const threadFile = getThreadFile(handle);
104
+
105
+ if (!fs.existsSync(threadFile)) {
106
+ return 0;
107
+ }
108
+
109
+ const countBefore = count(handle);
110
+ fs.unlinkSync(threadFile);
111
+ return countBefore;
112
+ }
113
+
114
+ /**
115
+ * List all threads with memories
116
+ * Returns array of { handle, count, lastUpdated }
117
+ */
118
+ function listThreads() {
119
+ ensureMemoryDir();
120
+
121
+ try {
122
+ const files = fs.readdirSync(MEMORY_DIR);
123
+ const threads = [];
124
+
125
+ for (const file of files) {
126
+ if (!file.startsWith('thread_') || !file.endsWith('.jsonl')) continue;
127
+
128
+ const handle = file.replace('thread_', '').replace('.jsonl', '');
129
+ const filePath = path.join(MEMORY_DIR, file);
130
+ const stats = fs.statSync(filePath);
131
+ const memories = recall(handle, 10000);
132
+
133
+ threads.push({
134
+ handle,
135
+ count: memories.length,
136
+ lastUpdated: stats.mtime,
137
+ oldestMemory: memories.length > 0 ? memories[memories.length - 1].timestamp : null,
138
+ newestMemory: memories.length > 0 ? memories[0].timestamp : null
139
+ });
140
+ }
141
+
142
+ // Sort by last updated, newest first
143
+ threads.sort((a, b) => b.lastUpdated - a.lastUpdated);
144
+ return threads;
145
+ } catch (e) {
146
+ return [];
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Get path to memory directory (for inspection)
152
+ */
153
+ function getMemoryPath() {
154
+ ensureMemoryDir();
155
+ return MEMORY_DIR;
156
+ }
157
+
158
+ module.exports = {
159
+ remember,
160
+ recall,
161
+ count,
162
+ forget,
163
+ listThreads,
164
+ getMemoryPath,
165
+ getThreadFile
166
+ };
@@ -0,0 +1,77 @@
1
+ /**
2
+ * MCP `list_changed` notification emitter
3
+ *
4
+ * Triggers Claude to refresh tool results without reconnection.
5
+ * Implements debouncing to prevent notification spam.
6
+ *
7
+ * This eliminates the need for 30-second polling loops,
8
+ * reducing API calls by ~90% and providing instant updates.
9
+ */
10
+
11
+ class NotificationEmitter {
12
+ constructor(server) {
13
+ this.server = server;
14
+ this.debounceTimers = {};
15
+ }
16
+
17
+ /**
18
+ * Emit list_changed notification with debouncing
19
+ * @param {string} reason - Why notification is being sent (for logging/debugging)
20
+ * @param {number} debounceMs - Debounce window in milliseconds (default: 1000ms)
21
+ */
22
+ emitChange(reason, debounceMs = 1000) {
23
+ // Debounce to prevent notification spam
24
+ // If we get multiple changes of the same type within the window,
25
+ // only emit one notification
26
+ if (this.debounceTimers[reason]) {
27
+ clearTimeout(this.debounceTimers[reason]);
28
+ }
29
+
30
+ this.debounceTimers[reason] = setTimeout(() => {
31
+ try {
32
+ this.server.notification({
33
+ method: "notifications/list_changed"
34
+ });
35
+ delete this.debounceTimers[reason];
36
+ } catch (e) {
37
+ // Silent fail - notifications are best-effort
38
+ // If notification fails, Claude will continue working normally
39
+ }
40
+ }, debounceMs);
41
+ }
42
+
43
+ /**
44
+ * Emit immediately without debouncing
45
+ * Use for urgent updates like direct mentions
46
+ */
47
+ emitImmediate() {
48
+ try {
49
+ this.server.notification({
50
+ method: "notifications/list_changed"
51
+ });
52
+ } catch (e) {
53
+ // Silent fail
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Cancel pending notifications for a specific reason
59
+ * Useful when shutting down or cleaning up
60
+ */
61
+ cancel(reason) {
62
+ if (this.debounceTimers[reason]) {
63
+ clearTimeout(this.debounceTimers[reason]);
64
+ delete this.debounceTimers[reason];
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Cancel all pending notifications
70
+ */
71
+ cancelAll() {
72
+ Object.values(this.debounceTimers).forEach(timer => clearTimeout(timer));
73
+ this.debounceTimers = {};
74
+ }
75
+ }
76
+
77
+ module.exports = NotificationEmitter;
package/notify.js CHANGED
@@ -89,6 +89,8 @@ async function checkAndNotify(inbox) {
89
89
 
90
90
  let notified = false;
91
91
 
92
+ const myHandle = config.getHandle();
93
+
92
94
  for (const msg of inbox) {
93
95
  // Skip if already notified
94
96
  if (state.notifiedIds.includes(msg.id)) continue;
@@ -96,6 +98,9 @@ async function checkAndNotify(inbox) {
96
98
  // Skip if already read
97
99
  if (msg.read) continue;
98
100
 
101
+ // Skip messages FROM yourself (don't notify about your own sends)
102
+ if (myHandle && msg.from?.toLowerCase() === myHandle.toLowerCase()) continue;
103
+
99
104
  const msgTime = new Date(msg.createdAt).getTime();
100
105
  const age = now - msgTime;
101
106
 
@@ -104,7 +109,6 @@ async function checkAndNotify(inbox) {
104
109
  let reason = '';
105
110
 
106
111
  // Rule 1: Direct mention in message (always if not "off")
107
- const myHandle = config.getHandle();
108
112
  if (myHandle && msg.text && msg.text.toLowerCase().includes(`@${myHandle}`)) {
109
113
  shouldNotify = true;
110
114
  reason = 'mention';
@@ -236,6 +240,89 @@ function checkPresence(activeUsers) {
236
240
  return justJoined;
237
241
  }
238
242
 
243
+ /**
244
+ * Notify about new ships from connections (FOMO/retention driver)
245
+ */
246
+ const SHIPS_STATE_FILE = path.join(config.VIBE_DIR, '.ships_state.json');
247
+
248
+ function loadShipsState() {
249
+ try {
250
+ if (fs.existsSync(SHIPS_STATE_FILE)) {
251
+ return JSON.parse(fs.readFileSync(SHIPS_STATE_FILE, 'utf8'));
252
+ }
253
+ } catch (e) {}
254
+ return { seenShipIds: [], lastCheck: null };
255
+ }
256
+
257
+ function saveShipsState(state) {
258
+ try {
259
+ fs.writeFileSync(SHIPS_STATE_FILE, JSON.stringify(state, null, 2));
260
+ } catch (e) {}
261
+ }
262
+
263
+ /**
264
+ * Check for new ships and notify
265
+ * Only notifies about ships from people in memory (connections)
266
+ */
267
+ async function checkShips(memoryHandles = []) {
268
+ const notifyLevel = config.getNotifications();
269
+ if (notifyLevel === 'off') return [];
270
+
271
+ const state = loadShipsState();
272
+ const now = Date.now();
273
+ const myHandle = config.getHandle();
274
+ const newShips = [];
275
+
276
+ try {
277
+ // Fetch recent ships from board API
278
+ const apiUrl = config.getApiUrl();
279
+ const response = await fetch(`${apiUrl}/api/board?limit=10&category=shipped`);
280
+ const data = await response.json();
281
+ const ships = data.entries || [];
282
+
283
+ for (const ship of ships) {
284
+ // Skip if already seen
285
+ const shipId = ship.id || `${ship.author}-${ship.timestamp}`;
286
+ if (state.seenShipIds.includes(shipId)) continue;
287
+
288
+ // Skip our own ships
289
+ if (ship.author === myHandle) {
290
+ state.seenShipIds.push(shipId);
291
+ continue;
292
+ }
293
+
294
+ // Only notify about ships from connections (people in memory)
295
+ const isConnection = memoryHandles.includes(ship.author.toLowerCase());
296
+
297
+ if (isConnection && notifyLevel === 'all') {
298
+ const content = ship.content?.slice(0, 60) || 'something new';
299
+ showNotification(
300
+ `/vibe — @${ship.author} shipped! 🚀`,
301
+ content,
302
+ false, // no system sound
303
+ true // terminal bell (soft nudge)
304
+ );
305
+ newShips.push(ship);
306
+ }
307
+
308
+ state.seenShipIds.push(shipId);
309
+ }
310
+
311
+ // Trim old ship IDs (keep last 50)
312
+ if (state.seenShipIds.length > 50) {
313
+ state.seenShipIds = state.seenShipIds.slice(-50);
314
+ }
315
+
316
+ state.lastCheck = now;
317
+ saveShipsState(state);
318
+
319
+ } catch (e) {
320
+ // Silent fail - notifications are best-effort
321
+ }
322
+
323
+ return newShips;
324
+ }
325
+
239
326
  /**
240
327
  * Unified notification check - call from any tool
241
328
  */
@@ -255,6 +342,19 @@ async function checkAll(store) {
255
342
  if (users.length > 0) {
256
343
  checkPresence(users);
257
344
  }
345
+
346
+ // Check for ships (only from connections)
347
+ // Get memory handles from local store
348
+ try {
349
+ const localStore = require('./store/local');
350
+ const memories = localStore.getAllThreadMemories ? localStore.getAllThreadMemories() : {};
351
+ const memoryHandles = Object.keys(memories).map(h => h.toLowerCase());
352
+ if (memoryHandles.length > 0) {
353
+ await checkShips(memoryHandles);
354
+ }
355
+ } catch (e) {
356
+ // Memory check is optional
357
+ }
258
358
  } catch (e) {
259
359
  // Silent fail - notifications are best-effort
260
360
  }
@@ -264,6 +364,7 @@ module.exports = {
264
364
  showNotification,
265
365
  checkAndNotify,
266
366
  checkPresence,
367
+ checkShips,
267
368
  checkAll,
268
369
  notify,
269
370
  ringBell
package/package.json CHANGED
@@ -1,14 +1,19 @@
1
1
  {
2
2
  "name": "slashvibe-mcp",
3
- "version": "0.2.9",
4
- "mcpName": "io.github.brightseth/vibe",
3
+ "version": "0.3.13",
4
+ "mcpName": "io.github.vibecodinginc/vibe",
5
5
  "description": "Social layer for Claude Code - DMs, presence, and connection between AI-assisted developers",
6
6
  "main": "index.js",
7
7
  "bin": {
8
- "slashvibe-mcp": "./index.js"
8
+ "slashvibe-mcp": "./index.js",
9
+ "vibe-setup": "./setup.js"
9
10
  },
10
11
  "scripts": {
11
- "start": "node index.js"
12
+ "start": "node index.js",
13
+ "test": "node --test tools/*.test.js",
14
+ "test:context": "node --test tools/_work-context.test.js",
15
+ "lint": "eslint . --ext .js",
16
+ "lint:fix": "eslint . --ext .js --fix"
12
17
  },
13
18
  "keywords": [
14
19
  "mcp",
@@ -23,22 +28,27 @@
23
28
  "license": "MIT",
24
29
  "repository": {
25
30
  "type": "git",
26
- "url": "git+https://github.com/spirit-protocol/vibe-mcp.git"
31
+ "url": "git+https://github.com/VibeCodingInc/vibe-mcp.git"
27
32
  },
28
33
  "homepage": "https://slashvibe.dev",
29
34
  "bugs": {
30
- "url": "https://github.com/spirit-protocol/vibe-mcp/issues"
35
+ "url": "https://github.com/VibeCodingInc/vibe-mcp/issues"
31
36
  },
32
37
  "engines": {
33
38
  "node": ">=18.0.0"
34
39
  },
35
40
  "files": [
36
41
  "index.js",
42
+ "setup.js",
43
+ "auth-store.js",
44
+ "auto-update.js",
37
45
  "config.js",
38
46
  "crypto.js",
39
47
  "discord.js",
40
48
  "memory.js",
41
49
  "notify.js",
50
+ "notification-emitter.js",
51
+ "analytics.js",
42
52
  "presence.js",
43
53
  "prompts.js",
44
54
  "twitter.js",
@@ -46,9 +56,13 @@
46
56
  "tools/",
47
57
  "store/",
48
58
  "protocol/",
59
+ "intelligence/",
60
+ "games/",
61
+ "bridges/",
49
62
  "README.md"
50
63
  ],
51
64
  "dependencies": {
52
- "crossword-layout-generator": "^0.1.1"
65
+ "crossword-layout-generator": "^0.1.1",
66
+ "node-fetch": "^2.7.0"
53
67
  }
54
68
  }
package/prompts.js CHANGED
@@ -68,7 +68,7 @@ function extractPatterns() {
68
68
 
69
69
  for (const p of prompts) {
70
70
  // Normalize: lowercase, replace @handles with @*, replace quoted strings with "*"
71
- let normalized = p.prompt.toLowerCase()
71
+ const normalized = p.prompt.toLowerCase()
72
72
  .replace(/@\w+/g, '@*')
73
73
  .replace(/"[^"]+"/g, '"*"')
74
74
  .replace(/'[^']+'/g, "'*'")
package/protocol/index.js CHANGED
@@ -101,10 +101,49 @@ const ACK_SCHEMA = {
101
101
  }
102
102
  };
103
103
 
104
+ /**
105
+ * Artifact schema — For sharing artifacts in messages
106
+ *
107
+ * Example:
108
+ * {
109
+ * type: 'artifact',
110
+ * version: '0.1.0',
111
+ * artifactId: 'artifact_1768035804429_1651c037',
112
+ * slug: 'pizza-guide-abc123',
113
+ * title: 'Stan's North Beach Pizza Guide',
114
+ * template: 'guide',
115
+ * preview: 'Best pizza spots in North Beach...',
116
+ * url: 'https://slashvibe.dev/a/pizza-guide-abc123'
117
+ * }
118
+ */
119
+ const ARTIFACT_SCHEMA = {
120
+ type: 'artifact',
121
+ required: ['artifactId', 'slug', 'title', 'template', 'url'],
122
+ validate: (payload) => {
123
+ if (!payload.artifactId || typeof payload.artifactId !== 'string') {
124
+ return { valid: false, error: 'Missing or invalid artifactId' };
125
+ }
126
+ if (!payload.slug || typeof payload.slug !== 'string') {
127
+ return { valid: false, error: 'Missing or invalid slug' };
128
+ }
129
+ if (!payload.title || typeof payload.title !== 'string') {
130
+ return { valid: false, error: 'Missing or invalid title' };
131
+ }
132
+ if (!['guide', 'learning', 'workspace'].includes(payload.template)) {
133
+ return { valid: false, error: 'Invalid template (must be: guide, learning, workspace)' };
134
+ }
135
+ if (!payload.url || typeof payload.url !== 'string') {
136
+ return { valid: false, error: 'Missing or invalid url' };
137
+ }
138
+ return { valid: true };
139
+ }
140
+ };
141
+
104
142
  const SCHEMAS = {
105
143
  game: GAME_SCHEMA,
106
144
  handoff: HANDOFF_SCHEMA,
107
- ack: ACK_SCHEMA
145
+ ack: ACK_SCHEMA,
146
+ artifact: ARTIFACT_SCHEMA
108
147
  };
109
148
 
110
149
  // ============ PROTOCOL FUNCTIONS ============
@@ -266,6 +305,10 @@ function formatPayload(payload) {
266
305
  return formatHandoffPayload(payload);
267
306
  case 'ack':
268
307
  return formatAckPayload(payload);
308
+ case 'artifact':
309
+ return formatArtifactPayload(payload);
310
+ case 'code':
311
+ return formatCodePayload(payload);
269
312
  default:
270
313
  return `📦 _${payload.type} payload_`;
271
314
  }
@@ -316,6 +359,117 @@ function formatAckPayload(payload) {
316
359
  return `${icon} Acknowledged: ${payload.replyTo} (${status})`;
317
360
  }
318
361
 
362
+ /**
363
+ * Format a code snippet payload for display
364
+ */
365
+ function formatCodePayload(payload) {
366
+ const lang = payload.language || '';
367
+ const filename = payload.filename || null;
368
+ const code = payload.code || '';
369
+ const description = payload.description || null;
370
+
371
+ let display = '📝 **Code Snippet**';
372
+ if (filename) {
373
+ display += ` — \`${filename}\``;
374
+ }
375
+ if (lang) {
376
+ display += ` (${lang})`;
377
+ }
378
+ display += '\n';
379
+
380
+ if (description) {
381
+ display += `> ${description}\n`;
382
+ }
383
+
384
+ display += `\`\`\`${lang}\n${code}\n\`\`\``;
385
+
386
+ return display;
387
+ }
388
+
389
+ /**
390
+ * Create a code snippet payload
391
+ * @param {string} code - The code content
392
+ * @param {object} options - Options
393
+ * @param {string} [options.language] - Programming language
394
+ * @param {string} [options.filename] - Original filename
395
+ * @param {string} [options.description] - Brief description
396
+ */
397
+ function createCodePayload(code, options = {}) {
398
+ return {
399
+ type: 'code',
400
+ version: PROTOCOL_VERSION,
401
+ code,
402
+ language: options.language || detectLanguage(code),
403
+ filename: options.filename || null,
404
+ description: options.description || null,
405
+ };
406
+ }
407
+
408
+ /**
409
+ * Simple language detection based on content patterns
410
+ */
411
+ function detectLanguage(code) {
412
+ if (!code) return '';
413
+
414
+ // Check for common patterns
415
+ if (code.includes('import React') || code.includes('useState') || code.includes('useEffect')) return 'jsx';
416
+ if (code.includes('import ') && code.includes(' from ')) return 'javascript';
417
+ if (code.includes('async function') || code.includes('await ')) return 'javascript';
418
+ if (code.includes('def ') && code.includes(':')) return 'python';
419
+ if (code.includes('func ') && code.includes('()')) return 'go';
420
+ if (code.includes('fn ') && code.includes('->')) return 'rust';
421
+ if (code.includes('SELECT ') || code.includes('INSERT INTO')) return 'sql';
422
+ if (code.includes('<!DOCTYPE') || code.includes('<html')) return 'html';
423
+ if (code.includes('{') && code.includes(':') && code.includes(';')) return 'css';
424
+ if (code.startsWith('{') && code.endsWith('}')) return 'json';
425
+ if (code.startsWith('#!') && code.includes('/bin/')) return 'bash';
426
+
427
+ return '';
428
+ }
429
+
430
+ function formatArtifactPayload(payload) {
431
+ const template = payload.template || 'artifact';
432
+ const templateIcon = template === 'guide' ? '📘' : template === 'learning' ? '💡' : template === 'workspace' ? '🗂️' : '📦';
433
+
434
+ let display = `${templateIcon} **${payload.title}**\n`;
435
+
436
+ if (payload.preview) {
437
+ const preview = payload.preview.length > 120 ? payload.preview.substring(0, 120) + '...' : payload.preview;
438
+ display += `> ${preview}\n\n`;
439
+ }
440
+
441
+ display += `🔗 [View artifact](${payload.url})`;
442
+
443
+ return display;
444
+ }
445
+
446
+ // ============ ARTIFACT HELPERS ============
447
+
448
+ /**
449
+ * Create an artifact card payload
450
+ * @param {Object} artifact - Artifact object from API
451
+ * @returns {Object} - Artifact payload
452
+ */
453
+ function createArtifactPayload(artifact) {
454
+ // Extract preview from first paragraph block
455
+ let preview = '';
456
+ if (artifact.content && artifact.content.blocks) {
457
+ const firstPara = artifact.content.blocks.find(b => b.type === 'paragraph');
458
+ if (firstPara && firstPara.markdown) {
459
+ preview = firstPara.markdown.substring(0, 150);
460
+ }
461
+ }
462
+
463
+ return createPayload('artifact', {
464
+ artifactId: artifact.id,
465
+ slug: artifact.slug,
466
+ title: artifact.title,
467
+ template: artifact.template,
468
+ preview: preview || undefined,
469
+ url: `https://slashvibe.dev/a/${artifact.slug}`
470
+ });
471
+ }
472
+
319
473
  module.exports = {
320
474
  PROTOCOL_VERSION,
321
475
 
@@ -335,6 +489,12 @@ module.exports = {
335
489
  // Ack helpers
336
490
  createAckPayload,
337
491
 
492
+ // Artifact helpers
493
+ createArtifactPayload,
494
+
495
+ // Code helpers
496
+ createCodePayload,
497
+
338
498
  // Schemas (for extension)
339
499
  SCHEMAS
340
500
  };