slashvibe-mcp 0.3.21 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/auto-update.js +10 -15
  4. package/config.js +36 -31
  5. package/crypto.js +1 -6
  6. package/debug.js +12 -0
  7. package/discord.js +19 -19
  8. package/eslint.config.js +54 -0
  9. package/index.js +217 -207
  10. package/intelligence/index.js +2 -9
  11. package/intelligence/infer.js +10 -16
  12. package/intelligence/patterns.js +23 -18
  13. package/intelligence/proactive.js +16 -15
  14. package/intelligence/serendipity.js +57 -20
  15. package/memory.js +13 -8
  16. package/migrate-v2.js +72 -0
  17. package/notification-emitter.js +2 -2
  18. package/notify.js +39 -14
  19. package/package.json +28 -29
  20. package/post-install.js +141 -0
  21. package/presence.js +2 -2
  22. package/prompts.js +5 -9
  23. package/protocol/index.js +123 -87
  24. package/protocol/telegram-commands.js +36 -37
  25. package/store/api.js +358 -529
  26. package/store/local.js +9 -10
  27. package/store/profiles.js +48 -192
  28. package/store/reservations.js +2 -9
  29. package/store/skills.js +69 -71
  30. package/store/sqlite.js +355 -0
  31. package/test-skills-bootstrap.js +20 -0
  32. package/test-v2-integration.js +385 -0
  33. package/tools/_actions.js +48 -387
  34. package/tools/_connection-queue.js +45 -56
  35. package/tools/_discovery-enhanced.js +52 -57
  36. package/tools/_discovery.js +87 -185
  37. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  38. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  39. package/tools/_proactive-discovery.js +60 -73
  40. package/tools/_shared/index.js +41 -64
  41. package/tools/admin-inbox.js +10 -15
  42. package/tools/agents.js +1 -1
  43. package/tools/artifact-create.js +13 -23
  44. package/tools/artifact-view.js +4 -4
  45. package/tools/{_deprecated/back.js → back.js} +1 -1
  46. package/tools/bye.js +3 -5
  47. package/tools/consent.js +2 -2
  48. package/tools/context.js +9 -10
  49. package/tools/crossword.js +3 -2
  50. package/tools/discover.js +94 -356
  51. package/tools/dm.js +27 -86
  52. package/tools/doctor.js +12 -41
  53. package/tools/drawing.js +34 -20
  54. package/tools/echo.js +11 -11
  55. package/tools/feed.js +30 -58
  56. package/tools/follow.js +64 -187
  57. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  58. package/tools/game.js +144 -48
  59. package/tools/handoff.js +6 -8
  60. package/tools/help.js +3 -3
  61. package/tools/idea.js +15 -27
  62. package/tools/inbox.js +121 -293
  63. package/tools/init.js +54 -151
  64. package/tools/invite.js +8 -21
  65. package/tools/migrate.js +27 -24
  66. package/tools/multiplayer-game.js +50 -40
  67. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  68. package/tools/notifications.js +58 -48
  69. package/tools/observe.js +12 -15
  70. package/tools/onboarding.js +8 -11
  71. package/tools/open.js +13 -144
  72. package/tools/party-game.js +23 -12
  73. package/tools/patterns.js +2 -1
  74. package/tools/ping.js +5 -7
  75. package/tools/react.js +28 -30
  76. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  77. package/tools/release.js +4 -2
  78. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  79. package/tools/report.js +2 -2
  80. package/tools/request.js +6 -26
  81. package/tools/reserve.js +1 -1
  82. package/tools/session-fork.js +97 -0
  83. package/tools/session-save.js +109 -0
  84. package/tools/settings.js +30 -99
  85. package/tools/ship.js +74 -56
  86. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  87. package/tools/social-inbox.js +22 -28
  88. package/tools/social-post.js +24 -27
  89. package/tools/solo-game.js +54 -46
  90. package/tools/start.js +14 -148
  91. package/tools/status.js +21 -68
  92. package/tools/submit.js +4 -2
  93. package/tools/suggest-tags.js +36 -33
  94. package/tools/summarize.js +19 -16
  95. package/tools/tag-suggestions.js +72 -73
  96. package/tools/test.js +1 -1
  97. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  98. package/tools/token.js +4 -4
  99. package/tools/update.js +1 -2
  100. package/tools/watch.js +132 -112
  101. package/tools/who.js +20 -40
  102. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  103. package/tools/workshop-buddy.js +52 -53
  104. package/tools/x-mentions.js +0 -1
  105. package/tools/x-reply.js +0 -1
  106. package/twitter.js +14 -20
  107. package/version.json +8 -10
  108. package/webhook-runner.js +132 -0
  109. package/auth-store.js +0 -148
  110. package/bridges/bridge-monitor.js +0 -388
  111. package/bridges/discord-bot.js +0 -431
  112. package/bridges/farcaster.js +0 -299
  113. package/bridges/telegram.js +0 -261
  114. package/bridges/webhook-health.js +0 -420
  115. package/bridges/webhook-server.js +0 -437
  116. package/bridges/whatsapp.js +0 -441
  117. package/bridges/x-webhook.js +0 -423
  118. package/games/arcade.js +0 -406
  119. package/games/chess.js +0 -451
  120. package/games/colorguess.js +0 -343
  121. package/games/crossword-words.js +0 -171
  122. package/games/crossword.js +0 -461
  123. package/games/drawing.js +0 -347
  124. package/games/gameroulette.js +0 -300
  125. package/games/gamerouter.js +0 -336
  126. package/games/gamestatus.js +0 -337
  127. package/games/guessnumber.js +0 -209
  128. package/games/hangman.js +0 -279
  129. package/games/memory.js +0 -338
  130. package/games/multiplayer-tictactoe.js +0 -389
  131. package/games/pixelart.js +0 -399
  132. package/games/quickduel.js +0 -354
  133. package/games/riddle.js +0 -371
  134. package/games/rockpaperscissors.js +0 -291
  135. package/games/snake.js +0 -406
  136. package/games/storybuilder.js +0 -343
  137. package/games/tictactoe.js +0 -345
  138. package/games/twentyquestions.js +0 -286
  139. package/games/twotruths.js +0 -207
  140. package/games/werewolf.js +0 -508
  141. package/games/wordassociation.js +0 -247
  142. package/games/wordchain.js +0 -135
  143. package/intelligence/interests.js +0 -369
  144. package/setup.js +0 -480
  145. package/smart-inbox.js +0 -276
  146. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  147. package/tools/_deprecated/bootstrap-skills.js +0 -231
  148. package/tools/_deprecated/bridge-dashboard.js +0 -342
  149. package/tools/_deprecated/bridge-health.js +0 -400
  150. package/tools/_deprecated/bridge-live.js +0 -384
  151. package/tools/_deprecated/bridges.js +0 -383
  152. package/tools/_deprecated/colorguess.js +0 -281
  153. package/tools/_deprecated/discover-insights.js +0 -379
  154. package/tools/_deprecated/discover-momentum.js +0 -256
  155. package/tools/_deprecated/discovery-analytics.js +0 -345
  156. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  157. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  158. package/tools/_deprecated/discovery-daily.js +0 -375
  159. package/tools/_deprecated/discovery-dashboard.js +0 -385
  160. package/tools/_deprecated/discovery-digest.js +0 -314
  161. package/tools/_deprecated/discovery-hub.js +0 -357
  162. package/tools/_deprecated/discovery-insights.js +0 -384
  163. package/tools/_deprecated/discovery-momentum.js +0 -281
  164. package/tools/_deprecated/discovery-monitor.js +0 -319
  165. package/tools/_deprecated/discovery-proactive.js +0 -300
  166. package/tools/_deprecated/draw.js +0 -317
  167. package/tools/_deprecated/farcaster.js +0 -307
  168. package/tools/_deprecated/games-catalog.js +0 -376
  169. package/tools/_deprecated/games.js +0 -313
  170. package/tools/_deprecated/guessnumber.js +0 -194
  171. package/tools/_deprecated/hangman.js +0 -129
  172. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  173. package/tools/_deprecated/riddle.js +0 -240
  174. package/tools/_deprecated/run-bootstrap.js +0 -69
  175. package/tools/_deprecated/skills-analytics.js +0 -349
  176. package/tools/_deprecated/skills-bootstrap.js +0 -301
  177. package/tools/_deprecated/skills-dashboard.js +0 -268
  178. package/tools/_deprecated/skills.js +0 -380
  179. package/tools/_deprecated/smart-intro.js +0 -353
  180. package/tools/_deprecated/storybuilder.js +0 -331
  181. package/tools/_deprecated/telegram-bot.js +0 -183
  182. package/tools/_deprecated/telegram-setup.js +0 -214
  183. package/tools/_deprecated/twentyquestions.js +0 -143
  184. package/tools/_shared.js +0 -234
  185. package/tools/_work-context.js +0 -338
  186. package/tools/_work-context.manual-test.js +0 -199
  187. package/tools/_work-context.test.js +0 -260
  188. package/tools/activity.js +0 -220
  189. package/tools/agent-treasury.js +0 -288
  190. package/tools/analytics.js +0 -191
  191. package/tools/approve.js +0 -197
  192. package/tools/arcade.js +0 -173
  193. package/tools/artifacts-price.js +0 -107
  194. package/tools/ask-expert.js +0 -160
  195. package/tools/available.js +0 -120
  196. package/tools/become-expert.js +0 -150
  197. package/tools/broadcast.js +0 -325
  198. package/tools/chat.js +0 -202
  199. package/tools/collaborative-drawing.js +0 -286
  200. package/tools/connection-status.js +0 -178
  201. package/tools/earnings.js +0 -126
  202. package/tools/friends.js +0 -207
  203. package/tools/genesis.js +0 -233
  204. package/tools/gig-browse.js +0 -206
  205. package/tools/gig-complete.js +0 -144
  206. package/tools/health.js +0 -87
  207. package/tools/leaderboard.js +0 -117
  208. package/tools/lib/git-apply.js +0 -206
  209. package/tools/lib/git-bundle.js +0 -407
  210. package/tools/mint.js +0 -377
  211. package/tools/plan.js +0 -225
  212. package/tools/profile.js +0 -219
  213. package/tools/proof-of-work.js +0 -144
  214. package/tools/pulse.js +0 -218
  215. package/tools/reply.js +0 -166
  216. package/tools/reputation.js +0 -175
  217. package/tools/schedule.js +0 -367
  218. package/tools/search-messages.js +0 -123
  219. package/tools/session.js +0 -467
  220. package/tools/session_price.js +0 -128
  221. package/tools/smart-check.js +0 -201
  222. package/tools/social-processor.js +0 -445
  223. package/tools/streak.js +0 -147
  224. package/tools/stuck.js +0 -297
  225. package/tools/subscribe.js +0 -148
  226. package/tools/subscriptions.js +0 -134
  227. package/tools/tip.js +0 -193
  228. package/tools/wallet.js +0 -269
  229. package/tools/webhook-test.js +0 -388
  230. package/tools/withdraw.js +0 -145
  231. package/tools/work-summary.js +0 -96
  232. package/tools/workshop.js +0 -327
  233. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  234. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  235. /package/tools/{_deprecated/away.js → away.js} +0 -0
@@ -7,7 +7,7 @@
7
7
  const config = require('../config');
8
8
  const store = require('../store');
9
9
  const { createGamePayload } = require('../protocol');
10
- const { requireInit } = require('./_shared');
10
+ const { requireInit, debug } = require('./_shared');
11
11
 
12
12
  // Import game implementations
13
13
  const hangman = require('../games/hangman');
@@ -16,14 +16,12 @@ const memory = require('../games/memory');
16
16
 
17
17
  // Post game results to board
18
18
  async function postSoloGameResult(player, game, won, score = null) {
19
- const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
19
+ const API_URL = config.getApiUrl();
20
20
 
21
21
  try {
22
22
  let content;
23
23
  if (won) {
24
- content = score
25
- ? `@${player} won ${game} with a score of ${score}! 🎉`
26
- : `@${player} won ${game}! 🎉`;
24
+ content = score ? `@${player} won ${game} with a score of ${score}! 🎉` : `@${player} won ${game}! 🎉`;
27
25
  } else {
28
26
  content = `@${player} played ${game}`;
29
27
  }
@@ -38,7 +36,7 @@ async function postSoloGameResult(player, game, won, score = null) {
38
36
  })
39
37
  });
40
38
  } catch (e) {
41
- console.error('[solo-game] Failed to post to board:', e.message);
39
+ debug('solo-game', 'Failed to post to board:', e.message);
42
40
  }
43
41
  }
44
42
 
@@ -55,7 +53,8 @@ const definition = {
55
53
  },
56
54
  action: {
57
55
  type: 'string',
58
- description: 'Game action: hangman(guess, hint, new, status) | rps(rock, paper, scissors, new, status) | memory(input, new, status)'
56
+ description:
57
+ 'Game action: hangman(guess, hint, new, status) | rps(rock, paper, scissors, new, status) | memory(input, new, status)'
59
58
  },
60
59
  guess: {
61
60
  type: 'string',
@@ -90,7 +89,7 @@ const definition = {
90
89
  async function getCurrentGameState(player, game) {
91
90
  // For solo games, we store state with a special key
92
91
  const thread = await store.getThread(player, `solo-${game}`);
93
-
92
+
94
93
  // Find the most recent game payload
95
94
  for (let i = thread.length - 1; i >= 0; i--) {
96
95
  const msg = thread[i];
@@ -146,9 +145,9 @@ async function handler(args) {
146
145
  // Start new game
147
146
  const gameDifficulty = difficulty || 'medium';
148
147
  gameState = hangman.createInitialHangmanState(gameDifficulty);
149
-
148
+
150
149
  await saveGameState(myHandle, 'hangman', gameState, `Started new ${gameDifficulty} hangman game!`);
151
-
150
+
152
151
  const payload = createGamePayload('hangman', gameState);
153
152
  return {
154
153
  display: `## New Hangman Game (${gameDifficulty})\n\n${formatHangmanPayload(payload)}\n\nUse \`vibe solo-game hangman --guess a\` to guess letters`
@@ -159,13 +158,13 @@ async function handler(args) {
159
158
  // Show current game state
160
159
  const payload = createGamePayload('hangman', gameState);
161
160
  let displayText = `## Hangman Game\n\n${formatHangmanPayload(payload)}`;
162
-
161
+
163
162
  if (gameState.gameOver) {
164
163
  displayText += `\n\nUse \`vibe solo-game hangman --action new\` to start a new game`;
165
164
  } else {
166
165
  displayText += `\n\nUse \`vibe solo-game hangman --guess X\` to guess a letter`;
167
166
  }
168
-
167
+
169
168
  return { display: displayText };
170
169
  }
171
170
 
@@ -178,19 +177,19 @@ async function handler(args) {
178
177
  if (action === 'guess' || guess) {
179
178
  // Make a guess
180
179
  const guessLetter = guess || action;
181
-
180
+
182
181
  if (!guessLetter) {
183
182
  return { display: 'Please specify a letter to guess: `vibe solo-game hangman --guess a`' };
184
183
  }
185
184
 
186
185
  const result = hangman.makeGuess(gameState, guessLetter);
187
-
186
+
188
187
  if (result.error) {
189
188
  return { display: `Error: ${result.error}` };
190
189
  }
191
190
 
192
191
  const newGameState = result.gameState;
193
-
192
+
194
193
  // Save updated state
195
194
  let message = `Guessed "${guessLetter.toUpperCase()}"`;
196
195
  if (newGameState.gameOver) {
@@ -202,18 +201,18 @@ async function handler(args) {
202
201
  message += ' - Game over! 💀';
203
202
  }
204
203
  }
205
-
204
+
206
205
  await saveGameState(myHandle, 'hangman', newGameState, message);
207
-
206
+
208
207
  const payload = createGamePayload('hangman', newGameState);
209
208
  let displayText = `## Hangman Game\n\n${formatHangmanPayload(payload)}`;
210
-
209
+
211
210
  if (newGameState.gameOver) {
212
211
  displayText += `\n\nUse \`vibe solo-game hangman --action new\` to start a new game`;
213
212
  } else {
214
213
  displayText += `\n\nUse \`vibe solo-game hangman --guess X\` to guess another letter`;
215
214
  }
216
-
215
+
217
216
  return { display: displayText };
218
217
  }
219
218
 
@@ -229,9 +228,9 @@ async function handler(args) {
229
228
  // Start new game
230
229
  const gameBestOf = bestof || 1;
231
230
  gameState = rps.createInitialRPSState(gameBestOf);
232
-
231
+
233
232
  await saveGameState(myHandle, 'rps', gameState, `Started new Rock Paper Scissors game (best of ${gameBestOf})!`);
234
-
233
+
235
234
  const payload = createGamePayload('rps', gameState);
236
235
  return {
237
236
  display: `## New Rock Paper Scissors Game${gameBestOf > 1 ? ` (Best of ${gameBestOf})` : ''}\n\n${formatRPSPayload(payload)}\n\nUse \`vibe solo-game rps --move rock\` (or paper/scissors) to play`
@@ -242,53 +241,57 @@ async function handler(args) {
242
241
  // Show current game state
243
242
  const payload = createGamePayload('rps', gameState);
244
243
  let displayText = `## Rock Paper Scissors${gameState.bestOf > 1 ? ` (Best of ${gameState.bestOf})` : ''}\n\n${formatRPSPayload(payload)}`;
245
-
244
+
246
245
  if (gameState.gameOver) {
247
246
  displayText += `\n\nUse \`vibe solo-game rps --action new\` to start a new game`;
248
247
  } else {
249
248
  displayText += `\n\nUse \`vibe solo-game rps --move rock\` (or paper/scissors) to make your move`;
250
249
  }
251
-
250
+
252
251
  return { display: displayText };
253
252
  }
254
253
 
255
254
  // Handle moves (rock, paper, scissors can be action or move parameter)
256
255
  const playerMove = move || action;
257
256
  if (playerMove && ['rock', 'paper', 'scissors'].includes(playerMove.toLowerCase())) {
258
-
259
257
  const result = rps.makeMove(gameState, playerMove);
260
-
258
+
261
259
  if (result.error) {
262
260
  return { display: `Error: ${result.error}` };
263
261
  }
264
262
 
265
263
  const newGameState = result.gameState;
266
-
264
+
267
265
  // Save updated state
268
266
  let message = `Played ${playerMove}`;
269
267
  if (newGameState.gameOver) {
270
268
  if (newGameState.winner === 'player') {
271
269
  message += ' - You won the game! 🎉';
272
270
  // Post to board for wins
273
- postSoloGameResult(myHandle, 'Rock Paper Scissors', true, `${newGameState.playerScore}-${newGameState.opponentScore}`);
271
+ postSoloGameResult(
272
+ myHandle,
273
+ 'Rock Paper Scissors',
274
+ true,
275
+ `${newGameState.playerScore}-${newGameState.opponentScore}`
276
+ );
274
277
  } else {
275
278
  message += ' - You lost the game! 💀';
276
279
  }
277
280
  } else {
278
281
  message += ` vs ${newGameState.lastRound.opponentChoice}`;
279
282
  }
280
-
283
+
281
284
  await saveGameState(myHandle, 'rps', newGameState, message);
282
-
285
+
283
286
  const payload = createGamePayload('rps', newGameState);
284
287
  let displayText = `## Rock Paper Scissors${newGameState.bestOf > 1 ? ` (Best of ${newGameState.bestOf})` : ''}\n\n${formatRPSPayload(payload)}`;
285
-
288
+
286
289
  if (newGameState.gameOver) {
287
290
  displayText += `\n\nUse \`vibe solo-game rps --action new\` to start a new game`;
288
291
  } else {
289
292
  displayText += `\n\nUse \`vibe solo-game rps --move rock\` (or paper/scissors) for the next round`;
290
293
  }
291
-
294
+
292
295
  return { display: displayText };
293
296
  }
294
297
 
@@ -304,9 +307,9 @@ async function handler(args) {
304
307
  // Start new game
305
308
  const gameDifficulty = difficulty || 'medium';
306
309
  gameState = memory.createInitialMemoryState(gameDifficulty);
307
-
310
+
308
311
  await saveGameState(myHandle, 'memory', gameState, `Started new ${gameDifficulty} memory pattern game!`);
309
-
312
+
310
313
  const payload = createGamePayload('memory', gameState);
311
314
  return {
312
315
  display: `## New Memory Pattern Game (${gameDifficulty})\n\n${formatMemoryPayload(payload)}`
@@ -317,26 +320,26 @@ async function handler(args) {
317
320
  // Show current game state
318
321
  const payload = createGamePayload('memory', gameState);
319
322
  let displayText = `## Memory Pattern Game\n\n${formatMemoryPayload(payload)}`;
320
-
323
+
321
324
  if (gameState.gameOver) {
322
325
  displayText += `\n\nUse \`vibe solo-game memory --action new\` to start a new game`;
323
326
  }
324
-
327
+
325
328
  return { display: displayText };
326
329
  }
327
330
 
328
331
  if (action === 'input') {
329
332
  // Start input phase
330
333
  const result = memory.startInput(gameState);
331
-
334
+
332
335
  if (result.error) {
333
336
  return { display: `Error: ${result.error}` };
334
337
  }
335
338
 
336
339
  const newGameState = result.gameState;
337
-
340
+
338
341
  await saveGameState(myHandle, 'memory', newGameState, 'Ready for input!');
339
-
342
+
340
343
  const payload = createGamePayload('memory', newGameState);
341
344
  return {
342
345
  display: `## Memory Pattern Game\n\n${formatMemoryPayload(payload)}`
@@ -346,20 +349,25 @@ async function handler(args) {
346
349
  if (pattern) {
347
350
  // Submit pattern guess
348
351
  const result = memory.submitPattern(gameState, pattern);
349
-
352
+
350
353
  if (result.error) {
351
354
  return { display: `Error: ${result.error}` };
352
355
  }
353
356
 
354
357
  const newGameState = result.gameState;
355
-
358
+
356
359
  // Save updated state
357
360
  let message = `Submitted pattern: ${pattern}`;
358
361
  if (newGameState.gameOver) {
359
362
  if (newGameState.won) {
360
363
  message += ' - You won! 🎉';
361
364
  // Post to board for wins
362
- postSoloGameResult(myHandle, 'Memory Pattern', true, `Level ${newGameState.maxLevelReached}, Score ${newGameState.score}`);
365
+ postSoloGameResult(
366
+ myHandle,
367
+ 'Memory Pattern',
368
+ true,
369
+ `Level ${newGameState.maxLevelReached}, Score ${newGameState.score}`
370
+ );
363
371
  } else {
364
372
  message += ' - Game over! 🧠';
365
373
  }
@@ -368,16 +376,16 @@ async function handler(args) {
368
376
  } else if (newGameState.lastResult === 'wrong') {
369
377
  message += ' - Wrong! Try again!';
370
378
  }
371
-
379
+
372
380
  await saveGameState(myHandle, 'memory', newGameState, message);
373
-
381
+
374
382
  const payload = createGamePayload('memory', newGameState);
375
383
  let displayText = `## Memory Pattern Game\n\n${formatMemoryPayload(payload)}`;
376
-
384
+
377
385
  if (newGameState.gameOver) {
378
386
  displayText += `\n\nUse \`vibe solo-game memory --action new\` to start a new game`;
379
387
  }
380
-
388
+
381
389
  return { display: displayText };
382
390
  }
383
391
 
@@ -387,4 +395,4 @@ async function handler(args) {
387
395
  return { display: 'Unknown game. Supported games: hangman, rps, memory' };
388
396
  }
389
397
 
390
- module.exports = { definition, handler };
398
+ module.exports = { definition, handler };
package/tools/start.js CHANGED
@@ -18,7 +18,6 @@ const notify = require('../notify');
18
18
  const patterns = require('../intelligence/patterns');
19
19
  const { actions, formatActions } = require('./_actions');
20
20
  const init = require('./init');
21
- const { gatherWithTimeout } = require('./_work-context');
22
21
 
23
22
  const REPO_DIR = path.join(process.env.HOME, '.vibe', 'vibe-repo');
24
23
 
@@ -170,7 +169,8 @@ function generateWelcomeCard({ handle, onlineCount, unreadCount, versionInfo })
170
169
 
171
170
  const definition = {
172
171
  name: 'vibe_start',
173
- description: 'Start socializing on /vibe. Use when user says "let\'s vibe", "start vibing", "who\'s around", or wants to connect with others.',
172
+ description:
173
+ 'Start socializing on /vibe. Use when user says "let\'s vibe", "start vibing", "who\'s around", or wants to connect with others.',
174
174
  inputSchema: {
175
175
  type: 'object',
176
176
  properties: {
@@ -180,7 +180,7 @@ const definition = {
180
180
  },
181
181
  building: {
182
182
  type: 'string',
183
- description: 'What you\'re working on (one line). Only needed if not already initialized.'
183
+ description: "What you're working on (one line). Only needed if not already initialized."
184
184
  }
185
185
  }
186
186
  }
@@ -192,7 +192,7 @@ async function handler(args) {
192
192
 
193
193
  // Step 1: Check if properly authenticated with OAuth
194
194
  // If not, redirect to init for GitHub auth flow (shows pre-auth banner + OAuth)
195
- if (!config.hasOAuth()) {
195
+ if (!config.hasPrivyAuth()) {
196
196
  return init.handler({
197
197
  handle: args.handle,
198
198
  one_liner: args.building
@@ -212,32 +212,6 @@ async function handler(args) {
212
212
  // Fetch version info early (non-blocking, cached)
213
213
  const versionInfo = await getVersionInfo().catch(() => null);
214
214
 
215
- // ═══════════════════════════════════════════════════════════════════════
216
- // AMBIENT CONTEXT: Gather work context and auto-set presence
217
- // ═══════════════════════════════════════════════════════════════════════
218
- let workContext = null;
219
- const autoContextEnabled = config.get('autoContext', true); // Opt-out via settings
220
-
221
- if (autoContextEnabled) {
222
- try {
223
- // Gather context with timeout (won't block startup)
224
- workContext = await gatherWithTimeout(2000);
225
-
226
- // Auto-set presence so others see what we're working on (silent, non-blocking)
227
- if (workContext?.suggestions?.brief) {
228
- // Use store.heartbeat directly instead of importing context tool
229
- // This avoids circular dependencies and is simpler
230
- store.heartbeat(myHandle, config.getOneLiner(), {
231
- note: workContext.suggestions.brief,
232
- branch: workContext.git?.branch || null
233
- }).catch(() => {}); // Silent fail - don't block
234
- }
235
- } catch (e) {
236
- // Silent fail - context is nice-to-have, not required
237
- workContext = null;
238
- }
239
- }
240
-
241
215
  // Log session start for patterns
242
216
  patterns.logSessionStart(myHandle);
243
217
 
@@ -252,12 +226,8 @@ async function handler(args) {
252
226
 
253
227
  // Step 3: Check inbox + trigger notifications
254
228
  let unreadCount = 0;
255
- let inboxThreads = [];
256
229
  try {
257
- // Fetch full inbox (not just count) so we can include summaries
258
- inboxThreads = await store.getInbox(myHandle);
259
- unreadCount = inboxThreads.reduce((sum, t) => sum + (t.unread || 0), 0);
260
-
230
+ unreadCount = await store.getUnreadCount(myHandle);
261
231
  if (unreadCount > 0) {
262
232
  // Check for messages needing desktop notification escalation
263
233
  const rawInbox = await store.getRawInbox(myHandle).catch(() => []);
@@ -267,24 +237,6 @@ async function handler(args) {
267
237
  }
268
238
  } catch (e) {}
269
239
 
270
- // Step 4: Get connection suggestions from API
271
- let suggestions = [];
272
- try {
273
- const apiUrl = config.getApiUrl();
274
- const suggestionsResponse = await fetch(`${apiUrl}/api/suggestions?user=${myHandle}&limit=3`, {
275
- headers: { 'User-Agent': 'vibe-mcp-client' }
276
- });
277
-
278
- if (suggestionsResponse.ok) {
279
- const data = await suggestionsResponse.json();
280
- if (data.success && data.suggestions) {
281
- suggestions = data.suggestions;
282
- }
283
- }
284
- } catch (e) {
285
- // Silent fail - suggestions are nice-to-have
286
- }
287
-
288
240
  // Generate the ASCII welcome card (matches init.js format)
289
241
  const welcomeCard = generateWelcomeCard({
290
242
  handle: myHandle,
@@ -296,46 +248,6 @@ async function handler(args) {
296
248
  // Build display with card + any additional info
297
249
  let display = welcomeCard;
298
250
 
299
- // Add who's online section (top 5 with what they're building)
300
- if (others.length > 0) {
301
- const top5 = others.slice(0, 5);
302
- display += `\n\n**🟢 Online now:**`;
303
- top5.forEach(u => {
304
- const status = u.status ? ` (${u.status})` : '';
305
- const building = u.one_liner || u.note || '';
306
- const truncated = building.length > 40 ? building.slice(0, 40) + '...' : building;
307
- display += `\n• @${u.handle}${status}${truncated ? ' — ' + truncated : ''}`;
308
- });
309
- if (others.length > 5) {
310
- display += `\n• _+${others.length - 5} more..._`;
311
- }
312
- }
313
-
314
- // Add unread messages section (if any)
315
- if (unreadCount > 0) {
316
- const unreadSenders = inboxThreads.filter(t => t.unread > 0);
317
- display += `\n\n**📬 Unread (${unreadCount}):**`;
318
- unreadSenders.slice(0, 3).forEach(t => {
319
- const preview = t.lastMessage ? t.lastMessage.slice(0, 50) : '';
320
- const truncated = preview.length > 50 ? preview + '...' : preview;
321
- display += `\n• @${t.handle} (${t.unread}) — "${truncated}"`;
322
- });
323
- if (unreadSenders.length > 3) {
324
- display += `\n• _+${unreadSenders.length - 3} more threads..._`;
325
- }
326
- }
327
-
328
- // Add connection suggestions (if any)
329
- if (suggestions.length > 0) {
330
- display += `\n\n**🤝 Suggested connections:**`;
331
- suggestions.slice(0, 3).forEach(s => {
332
- display += `\n• @${s.handle} — ${s.reason}`;
333
- if (s.github_name) {
334
- display += ` (${s.github_name})`;
335
- }
336
- });
337
- }
338
-
339
251
  // Add memory context for returning users
340
252
  if (threads.length > 0) {
341
253
  const recentThreads = threads.slice(0, 3);
@@ -348,48 +260,17 @@ async function handler(args) {
348
260
  display += updateNotice;
349
261
  }
350
262
 
351
- // Step 6: Show rotating tips about features
352
- const tips = [
353
- '💡 **Tip:** Say "vibe stuck" when you need help — others can jump in and assist.',
354
- '💡 **Tip:** Use "vibe available \'React, auth\'" to signal you\'re open to chat about topics.',
355
- '💡 **Tip:** Use "vibe context --file auth.js" to share what you\'re working on.',
356
- '💡 **Tip:** Say "start presence monitor" for real-time alerts when people come online.'
357
- ];
358
- const tipIndex = Math.floor(Date.now() / 60000) % tips.length; // Rotate every minute
359
- display += `\n\n---\n${tips[tipIndex]}`;
263
+ // Step 6: Suggest background presence monitor (if not running)
264
+ const presenceAgentEnabled = config.get('presenceAgentEnabled', true);
265
+ const presenceAgentRunning = config.get('presenceAgentRunning');
266
+
267
+ if (presenceAgentEnabled && !presenceAgentRunning && others.length > 0) {
268
+ display += `\n\n---\n💡 **Tip:** Say "start presence monitor" for real-time alerts when interesting people come online.`;
269
+ }
360
270
 
361
271
  // Build response with hints for structured dashboard flow
362
272
  const response = { display };
363
273
 
364
- // === ENRICHED DATA ===
365
- // Include full online users list so Claude doesn't need to call vibe_who
366
- response.onlineUsers = others.map(u => ({
367
- handle: u.handle,
368
- building: u.one_liner || u.note || null,
369
- status: u.status || null,
370
- lastActive: u.lastSeen ? new Date(u.lastSeen).toISOString() : null
371
- }));
372
-
373
- // Include unread thread summaries so Claude doesn't need to call vibe_inbox
374
- const unreadSenders = inboxThreads.filter(t => t.unread > 0);
375
- response.unreadThreads = unreadSenders.map(t => ({
376
- handle: t.handle,
377
- unread: t.unread,
378
- preview: t.lastMessage ? t.lastMessage.slice(0, 80) : null,
379
- isAgent: t.isAgent || false
380
- }));
381
-
382
- // Include connection suggestions for smart discovery
383
- if (suggestions.length > 0) {
384
- response.suggestions = suggestions.map(s => ({
385
- handle: s.handle,
386
- reason: s.reason,
387
- githubName: s.github_name || null,
388
- lastActive: s.last_active || null,
389
- matchScore: s.score || null
390
- }));
391
- }
392
-
393
274
  // Determine session state and suggest appropriate flow
394
275
  let suggestion = null;
395
276
 
@@ -424,33 +305,18 @@ async function handler(args) {
424
305
 
425
306
  if (others.length === 0 && unreadCount === 0) {
426
307
  // Empty room
427
- actionList = actions.emptyRoom({ workContext });
308
+ actionList = actions.emptyRoom();
428
309
  } else {
429
310
  // Normal dashboard
430
311
  actionList = actions.dashboard({
431
312
  unreadCount,
432
313
  onlineUsers: onlineHandles,
433
- suggestion,
434
- workContext
314
+ suggestion
435
315
  });
436
316
  }
437
317
 
438
318
  response.actions = formatActions(actionList);
439
319
 
440
- // ═══════════════════════════════════════════════════════════════════════
441
- // WORK CONTEXT: Include in response for Claude to use
442
- // ═══════════════════════════════════════════════════════════════════════
443
- if (workContext?.suggestions?.brief) {
444
- response.workContext = {
445
- summary: workContext.suggestions.brief,
446
- detailed: workContext.suggestions.detailed,
447
- project: workContext.project?.name,
448
- branch: workContext.git?.branch,
449
- recentCommit: workContext.git?.recentCommits?.[0]?.message || null,
450
- hasUncommitted: workContext.git?.hasUncommitted || false
451
- };
452
- }
453
-
454
320
  return response;
455
321
  }
456
322
 
package/tools/status.js CHANGED
@@ -1,27 +1,23 @@
1
1
  /**
2
2
  * vibe status — Set your mood/status
3
- *
4
- * Now includes away/back functionality (previously vibe_away/vibe_back)
5
3
  */
6
4
 
5
+ const { requireInit } = require('./_shared');
7
6
  const config = require('../config');
8
7
  const store = require('../store');
9
8
  const discord = require('../discord');
10
9
  const { trackMood } = require('./summarize');
11
- const { markAway, markBack, getProactiveSummary } = require('../intelligence/proactive');
12
10
 
13
11
  const MOODS = {
14
- 'shipping': '🔥',
15
- 'thinking': '🧠',
16
- 'afk': '☕',
17
- 'debugging': '🐛',
18
- 'pairing': '👯',
19
- 'deep': '🎧',
20
- 'celebrating': '🎉',
21
- 'struggling': '😤',
22
- 'away': '☕', // Merged from vibe_away
23
- 'back': null, // Merged from vibe_back (clears status)
24
- 'clear': null
12
+ shipping: '🔥',
13
+ thinking: '🧠',
14
+ afk: '☕',
15
+ debugging: '🐛',
16
+ pairing: '👯',
17
+ deep: '🎧',
18
+ celebrating: '🎉',
19
+ struggling: '😤',
20
+ clear: null
25
21
  };
26
22
 
27
23
  // Special modes that toggle settings
@@ -29,17 +25,14 @@ const SPECIAL_MODES = ['guided', 'freeform'];
29
25
 
30
26
  const definition = {
31
27
  name: 'vibe_status',
32
- description: 'Set your mood/status. Options: shipping, thinking, afk, debugging, pairing, deep, celebrating, struggling, away, back, clear',
28
+ description:
29
+ 'Set your mood/status. Options: shipping, thinking, afk, debugging, pairing, deep, celebrating, struggling, clear',
33
30
  inputSchema: {
34
31
  type: 'object',
35
32
  properties: {
36
33
  mood: {
37
34
  type: 'string',
38
- description: 'Your mood (shipping, thinking, afk, debugging, pairing, deep, celebrating, struggling, away, back, clear)'
39
- },
40
- message: {
41
- type: 'string',
42
- description: 'Optional away message (only used with away mood, e.g., "grabbing coffee")'
35
+ description: 'Your mood (shipping, thinking, afk, debugging, pairing, deep, celebrating, struggling, clear)'
43
36
  }
44
37
  },
45
38
  required: ['mood']
@@ -47,15 +40,11 @@ const definition = {
47
40
  };
48
41
 
49
42
  async function handler(args) {
50
- if (!config.isInitialized()) {
51
- return {
52
- display: 'Run `vibe init` first to set your identity.'
53
- };
54
- }
43
+ const initCheck = requireInit();
44
+ if (initCheck) return initCheck;
55
45
 
56
- const { mood, message } = args;
46
+ const { mood } = args;
57
47
  const moodKey = mood.toLowerCase().replace(/[^a-z]/g, '');
58
- const handle = config.getHandle();
59
48
 
60
49
  // Handle special modes (guided/freeform)
61
50
  if (moodKey === 'guided') {
@@ -82,58 +71,22 @@ _Say "set status guided" to re-enable interactive menus._`
82
71
  };
83
72
  }
84
73
 
85
- // Handle 'away' mood (merged from vibe_away)
86
- if (moodKey === 'away') {
87
- const awayMessage = message?.trim();
88
-
89
- // Validate message length
90
- if (awayMessage && awayMessage.length > 100) {
91
- return { display: '⚠️ Away message too long (100 char max)' };
92
- }
93
-
94
- // Set away status with optional message
95
- await store.setAwayStatus(handle, 'away', awayMessage || null);
96
- markAway();
97
-
98
- if (awayMessage) {
99
- return { display: `☕ away — "${awayMessage}"` };
100
- }
101
- return { display: '☕ away' };
102
- }
103
-
104
- // Handle 'back' mood (merged from vibe_back)
105
- if (moodKey === 'back') {
106
- // Clear away status
107
- await store.clearAwayStatus(handle);
108
-
109
- let display = '👋 back';
110
-
111
- // Check if anything happened while away
112
- const unreadCount = await store.getUnreadCount(handle).catch(() => 0);
113
- if (unreadCount > 0) {
114
- display += ` — ${unreadCount} unread`;
115
- }
116
-
117
- // Mark as back for proactive tracking
118
- markBack();
119
-
120
- return { display };
121
- }
122
-
123
74
  if (!MOODS.hasOwnProperty(moodKey)) {
124
75
  const options = Object.entries(MOODS)
125
- .filter(([k, v]) => v && k !== 'back') // Exclude 'back' from list (it's a clear action)
76
+ .filter(([k, v]) => v)
126
77
  .map(([k, v]) => `${v} ${k}`)
127
78
  .join(', ');
128
79
  return {
129
- display: `Unknown mood. Options: ${options}, or "clear"/"back" to remove`
80
+ display: `Unknown mood. Options: ${options}, or "clear" to remove`
130
81
  };
131
82
  }
132
83
 
133
84
  const emoji = MOODS[moodKey];
85
+ const handle = config.getHandle();
134
86
 
135
87
  // Update presence with mood via context
136
- await store.heartbeat(handle, config.getOneLiner(), { mood: emoji });
88
+ // Phase 1 Presence Bridge: include source so platform knows this came from MCP
89
+ await store.heartbeat(handle, config.getOneLiner(), { mood: emoji }, 'mcp');
137
90
 
138
91
  // Track for session summary
139
92
  if (emoji) {