slashvibe-mcp 0.2.8 → 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 (161) hide show
  1. package/README.md +41 -58
  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 +27 -15
  14. package/games/arcade.js +406 -0
  15. package/games/chess.js +451 -0
  16. package/games/colorguess.js +343 -0
  17. package/games/crossword-words.js +171 -0
  18. package/games/crossword.js +461 -0
  19. package/games/drawing.js +347 -0
  20. package/games/gameroulette.js +300 -0
  21. package/games/gamerouter.js +336 -0
  22. package/games/gamestatus.js +337 -0
  23. package/games/guessnumber.js +209 -0
  24. package/games/hangman.js +279 -0
  25. package/games/memory.js +338 -0
  26. package/games/multiplayer-tictactoe.js +389 -0
  27. package/games/pixelart.js +399 -0
  28. package/games/quickduel.js +354 -0
  29. package/games/riddle.js +371 -0
  30. package/games/rockpaperscissors.js +291 -0
  31. package/games/snake.js +406 -0
  32. package/games/storybuilder.js +343 -0
  33. package/games/tictactoe.js +345 -0
  34. package/games/twentyquestions.js +286 -0
  35. package/games/twotruths.js +207 -0
  36. package/games/werewolf.js +508 -0
  37. package/games/wordassociation.js +247 -0
  38. package/games/wordchain.js +135 -0
  39. package/index.js +77 -53
  40. package/intelligence/index.js +9 -2
  41. package/intelligence/interests.js +369 -0
  42. package/notification-emitter.js +77 -0
  43. package/notify.js +5 -1
  44. package/package.json +18 -6
  45. package/prompts.js +1 -1
  46. package/protocol/index.js +73 -0
  47. package/setup.js +402 -0
  48. package/store/api.js +436 -211
  49. package/store/profiles.js +160 -12
  50. package/tools/_actions.js +362 -21
  51. package/tools/_discovery.js +119 -26
  52. package/tools/_shared/index.js +64 -0
  53. package/tools/_shared.js +234 -0
  54. package/tools/_work-context.js +338 -0
  55. package/tools/_work-context.manual-test.js +199 -0
  56. package/tools/_work-context.test.js +260 -0
  57. package/tools/activity.js +220 -0
  58. package/tools/analytics.js +191 -0
  59. package/tools/approve.js +197 -0
  60. package/tools/artifact-create.js +14 -3
  61. package/tools/artifacts-price.js +107 -0
  62. package/tools/available.js +120 -0
  63. package/tools/broadcast.js +286 -0
  64. package/tools/chat.js +202 -0
  65. package/tools/collaborative-drawing.js +1 -1
  66. package/tools/connection-status.js +178 -0
  67. package/tools/discover.js +350 -34
  68. package/tools/dm.js +80 -8
  69. package/tools/earnings.js +126 -0
  70. package/tools/feed.js +35 -4
  71. package/tools/follow.js +224 -0
  72. package/tools/friends.js +207 -0
  73. package/tools/gig-browse.js +206 -0
  74. package/tools/gig-complete.js +144 -0
  75. package/tools/help.js +3 -3
  76. package/tools/idea.js +9 -2
  77. package/tools/inbox.js +289 -105
  78. package/tools/init.js +106 -27
  79. package/tools/invite.js +15 -4
  80. package/tools/migrate.js +3 -3
  81. package/tools/multiplayer-game.js +1 -1
  82. package/tools/onboarding.js +7 -7
  83. package/tools/open.js +143 -12
  84. package/tools/party-game.js +1 -1
  85. package/tools/plan.js +225 -0
  86. package/tools/proof-of-work.js +144 -0
  87. package/tools/reply.js +166 -0
  88. package/tools/report.js +1 -1
  89. package/tools/request.js +17 -3
  90. package/tools/schedule.js +367 -0
  91. package/tools/search-messages.js +123 -0
  92. package/tools/session.js +420 -0
  93. package/tools/session_price.js +128 -0
  94. package/tools/settings.js +90 -2
  95. package/tools/ship.js +30 -7
  96. package/tools/smart-check.js +201 -0
  97. package/tools/start.js +147 -12
  98. package/tools/status.js +53 -6
  99. package/tools/stuck.js +297 -0
  100. package/tools/subscribe.js +148 -0
  101. package/tools/subscriptions.js +134 -0
  102. package/tools/suggest-tags.js +6 -8
  103. package/tools/tag-suggestions.js +1 -1
  104. package/tools/tip.js +150 -77
  105. package/tools/token.js +4 -4
  106. package/tools/update.js +1 -1
  107. package/tools/wallet.js +221 -79
  108. package/tools/watch.js +157 -0
  109. package/tools/who.js +30 -1
  110. package/tools/withdraw.js +145 -0
  111. package/tools/work-summary.js +96 -0
  112. package/version.json +10 -8
  113. package/LICENSE +0 -21
  114. package/store/sqlite.js +0 -347
  115. /package/tools/{auto-suggest-connections.js → _deprecated/auto-suggest-connections.js} +0 -0
  116. /package/tools/{away.js → _deprecated/away.js} +0 -0
  117. /package/tools/{back.js → _deprecated/back.js} +0 -0
  118. /package/tools/{bootstrap-skills.js → _deprecated/bootstrap-skills.js} +0 -0
  119. /package/tools/{bridge-dashboard.js → _deprecated/bridge-dashboard.js} +0 -0
  120. /package/tools/{bridge-health.js → _deprecated/bridge-health.js} +0 -0
  121. /package/tools/{bridge-live.js → _deprecated/bridge-live.js} +0 -0
  122. /package/tools/{bridges.js → _deprecated/bridges.js} +0 -0
  123. /package/tools/{colorguess.js → _deprecated/colorguess.js} +0 -0
  124. /package/tools/{discover-insights.js → _deprecated/discover-insights.js} +0 -0
  125. /package/tools/{discover-momentum.js → _deprecated/discover-momentum.js} +0 -0
  126. /package/tools/{discovery-analytics.js → _deprecated/discovery-analytics.js} +0 -0
  127. /package/tools/{discovery-auto-suggest.js → _deprecated/discovery-auto-suggest.js} +0 -0
  128. /package/tools/{discovery-bootstrap.js → _deprecated/discovery-bootstrap.js} +0 -0
  129. /package/tools/{discovery-daily.js → _deprecated/discovery-daily.js} +0 -0
  130. /package/tools/{discovery-dashboard.js → _deprecated/discovery-dashboard.js} +0 -0
  131. /package/tools/{discovery-digest.js → _deprecated/discovery-digest.js} +0 -0
  132. /package/tools/{discovery-hub.js → _deprecated/discovery-hub.js} +0 -0
  133. /package/tools/{discovery-insights.js → _deprecated/discovery-insights.js} +0 -0
  134. /package/tools/{discovery-momentum.js → _deprecated/discovery-momentum.js} +0 -0
  135. /package/tools/{discovery-monitor.js → _deprecated/discovery-monitor.js} +0 -0
  136. /package/tools/{discovery-proactive.js → _deprecated/discovery-proactive.js} +0 -0
  137. /package/tools/{draw.js → _deprecated/draw.js} +0 -0
  138. /package/tools/{farcaster.js → _deprecated/farcaster.js} +0 -0
  139. /package/tools/{forget.js → _deprecated/forget.js} +0 -0
  140. /package/tools/{games-catalog.js → _deprecated/games-catalog.js} +0 -0
  141. /package/tools/{games.js → _deprecated/games.js} +0 -0
  142. /package/tools/{guessnumber.js → _deprecated/guessnumber.js} +0 -0
  143. /package/tools/{hangman.js → _deprecated/hangman.js} +0 -0
  144. /package/tools/{multiplayer-tictactoe.js → _deprecated/multiplayer-tictactoe.js} +0 -0
  145. /package/tools/{mute.js → _deprecated/mute.js} +0 -0
  146. /package/tools/{recall.js → _deprecated/recall.js} +0 -0
  147. /package/tools/{remember.js → _deprecated/remember.js} +0 -0
  148. /package/tools/{riddle.js → _deprecated/riddle.js} +0 -0
  149. /package/tools/{run-bootstrap.js → _deprecated/run-bootstrap.js} +0 -0
  150. /package/tools/{skills-analytics.js → _deprecated/skills-analytics.js} +0 -0
  151. /package/tools/{skills-bootstrap.js → _deprecated/skills-bootstrap.js} +0 -0
  152. /package/tools/{skills-dashboard.js → _deprecated/skills-dashboard.js} +0 -0
  153. /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
  154. /package/tools/{skills.js → _deprecated/skills.js} +0 -0
  155. /package/tools/{smart-intro.js → _deprecated/smart-intro.js} +0 -0
  156. /package/tools/{storybuilder.js → _deprecated/storybuilder.js} +0 -0
  157. /package/tools/{telegram-bot.js → _deprecated/telegram-bot.js} +0 -0
  158. /package/tools/{telegram-setup.js → _deprecated/telegram-setup.js} +0 -0
  159. /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
  160. /package/tools/{twentyquestions.js → _deprecated/twentyquestions.js} +0 -0
  161. /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Word Association game implementation for /vibe
3
+ * Players take turns saying words that associate with the previous word
4
+ * Build creative chains of connected ideas!
5
+ */
6
+
7
+ // Simple word validation
8
+ function isValidWord(word) {
9
+ // Basic checks: alphabetic, reasonable length, not too short
10
+ if (!/^[a-zA-Z\-']+$/.test(word)) return false; // Allow hyphens and apostrophes
11
+ if (word.length < 2) return false;
12
+ if (word.length > 30) return false;
13
+
14
+ // Allow single meaningful words
15
+ if (word.length === 1) {
16
+ return ['a', 'i'].includes(word.toLowerCase());
17
+ }
18
+
19
+ return true;
20
+ }
21
+
22
+ // Create initial word association state
23
+ function createInitialWordAssociationState() {
24
+ return {
25
+ words: [],
26
+ players: [],
27
+ currentPlayer: null,
28
+ moves: 0,
29
+ gameOver: false,
30
+ winner: null,
31
+ lastWord: null,
32
+ usedWords: new Set(), // Track words to prevent exact repeats
33
+ playerTurnIndex: 0,
34
+ maxWords: 30, // End game after 30 words for now
35
+ associations: [], // Store word -> word pairs for fun stats
36
+ startedAt: new Date().toISOString()
37
+ };
38
+ }
39
+
40
+ // Add player to game
41
+ function addPlayer(gameState, playerHandle) {
42
+ if (gameState.players.includes(playerHandle)) {
43
+ return { error: 'Player already in the game' };
44
+ }
45
+
46
+ if (gameState.players.length >= 6) {
47
+ return { error: 'Game is full (max 6 players)' };
48
+ }
49
+
50
+ const newPlayers = [...gameState.players, playerHandle];
51
+
52
+ return {
53
+ success: true,
54
+ gameState: {
55
+ ...gameState,
56
+ players: newPlayers,
57
+ currentPlayer: gameState.currentPlayer || playerHandle // First player starts
58
+ }
59
+ };
60
+ }
61
+
62
+ // Make a word association move
63
+ function makeMove(gameState, word, playerHandle) {
64
+ const { words, players, currentPlayer, moves, usedWords, lastWord, maxWords } = gameState;
65
+
66
+ // Normalize word
67
+ const normalizedWord = word.toLowerCase().trim().replace(/['"]/g, '');
68
+
69
+ // Validate basic word format
70
+ if (!isValidWord(normalizedWord)) {
71
+ return { error: 'Invalid word format. Use only letters (2-30 characters).' };
72
+ }
73
+
74
+ // Check if it's the right player's turn (if multiplayer)
75
+ if (players.length > 1 && currentPlayer !== playerHandle) {
76
+ return { error: `Not your turn! Waiting for @${currentPlayer}` };
77
+ }
78
+
79
+ // Check if word was already used (allow some flexibility)
80
+ if (usedWords.has(normalizedWord)) {
81
+ return { error: 'Word already used! Try a different word.' };
82
+ }
83
+
84
+ // Check if it's the same word as last word
85
+ if (lastWord && normalizedWord === lastWord.toLowerCase()) {
86
+ return { error: 'Cannot repeat the same word immediately!' };
87
+ }
88
+
89
+ // Valid move - update game state
90
+ const newWords = [...words, { word: normalizedWord, player: playerHandle, timestamp: new Date().toISOString() }];
91
+ const newUsedWords = new Set([...usedWords, normalizedWord]);
92
+ const newAssociations = lastWord ? [...gameState.associations, `${lastWord} → ${normalizedWord}`] : gameState.associations;
93
+
94
+ // Determine next player
95
+ let nextPlayerIndex = gameState.playerTurnIndex;
96
+ let nextPlayer = currentPlayer;
97
+
98
+ if (players.length > 1) {
99
+ nextPlayerIndex = (gameState.playerTurnIndex + 1) % players.length;
100
+ nextPlayer = players[nextPlayerIndex];
101
+ }
102
+
103
+ // Check if game should end
104
+ const shouldEnd = moves + 1 >= maxWords;
105
+
106
+ const newGameState = {
107
+ ...gameState,
108
+ words: newWords,
109
+ currentPlayer: shouldEnd ? null : nextPlayer,
110
+ playerTurnIndex: nextPlayerIndex,
111
+ moves: moves + 1,
112
+ gameOver: shouldEnd,
113
+ winner: shouldEnd ? 'everyone' : null,
114
+ lastWord: normalizedWord,
115
+ usedWords: newUsedWords,
116
+ associations: newAssociations
117
+ };
118
+
119
+ return { success: true, gameState: newGameState };
120
+ }
121
+
122
+ // Format word association for display
123
+ function formatWordAssociationDisplay(gameState) {
124
+ const { words, players, currentPlayer, moves, gameOver, maxWords, associations } = gameState;
125
+
126
+ let display = `🧠 **Word Association** (${moves}/${maxWords} words)\n\n`;
127
+
128
+ if (gameOver) {
129
+ display += '🎉 **Game Complete!** What a creative journey!\n\n';
130
+ }
131
+
132
+ if (words.length === 0) {
133
+ if (players.length === 0) {
134
+ display += '**Start with any word!**\n\n';
135
+ display += 'Word association is about connecting ideas. Each new word should relate to the previous one.\n';
136
+ } else {
137
+ display += `**@${currentPlayer}, start us off with any word!**\n\n`;
138
+ }
139
+ } else {
140
+ // Show the association chain (last 8 words to keep it readable)
141
+ const recentWords = words.slice(-8);
142
+ const wordChain = recentWords.map(w => {
143
+ const capitalizedWord = w.word.charAt(0).toUpperCase() + w.word.slice(1);
144
+ return `**${capitalizedWord}** (@${w.player})`;
145
+ }).join(' → ');
146
+
147
+ display += '**Chain:** ' + wordChain;
148
+
149
+ if (words.length > 8) {
150
+ display += ` (showing last 8 of ${words.length})`;
151
+ }
152
+
153
+ display += '\n\n';
154
+
155
+ if (!gameOver) {
156
+ const lastWordEntry = words[words.length - 1];
157
+ const lastWord = lastWordEntry.word;
158
+ display += `**"${lastWord.charAt(0).toUpperCase() + lastWord.slice(1)}" makes you think of...**\n\n`;
159
+
160
+ if (players.length > 1) {
161
+ display += `Turn: **@${currentPlayer}**`;
162
+ } else {
163
+ display += '**Your turn!** What word comes to mind?';
164
+ }
165
+ }
166
+ }
167
+
168
+ // Show players if multiplayer
169
+ if (players.length > 1) {
170
+ display += `\n\n**Players (${players.length}):** ${players.map(p => `@${p}`).join(', ')}`;
171
+ }
172
+
173
+ // Show some fun stats at the end
174
+ if (gameOver && associations.length > 0) {
175
+ display += '\n\n**Creative Journey:**\n';
176
+ // Show the full chain condensed
177
+ const fullChain = words.map(w => w.word.charAt(0).toUpperCase() + w.word.slice(1)).join(' → ');
178
+ display += fullChain;
179
+ }
180
+
181
+ return display;
182
+ }
183
+
184
+ // Get game stats for fun
185
+ function getGameStats(gameState) {
186
+ const { words, players, associations } = gameState;
187
+
188
+ if (words.length === 0) return null;
189
+
190
+ // Player contribution stats
191
+ const contributions = {};
192
+ words.forEach(w => {
193
+ contributions[w.player] = (contributions[w.player] || 0) + 1;
194
+ });
195
+
196
+ return {
197
+ totalWords: words.length,
198
+ contributors: Object.keys(contributions).length,
199
+ contributions,
200
+ longestWord: Math.max(...words.map(w => w.word.length)),
201
+ shortestWord: Math.min(...words.map(w => w.word.length)),
202
+ uniqueWords: new Set(words.map(w => w.word)).size
203
+ };
204
+ }
205
+
206
+ // Check for interesting patterns or themes
207
+ function findThemes(gameState) {
208
+ const { words } = gameState;
209
+
210
+ if (words.length < 3) return [];
211
+
212
+ const themes = [];
213
+ const wordList = words.map(w => w.word.toLowerCase());
214
+
215
+ // Look for color words
216
+ const colors = ['red', 'blue', 'green', 'yellow', 'orange', 'purple', 'pink', 'black', 'white', 'brown', 'gray', 'grey'];
217
+ const colorWords = wordList.filter(w => colors.includes(w));
218
+ if (colorWords.length >= 2) {
219
+ themes.push(`🌈 Colors: ${colorWords.join(', ')}`);
220
+ }
221
+
222
+ // Look for animal words
223
+ const animals = ['cat', 'dog', 'bird', 'fish', 'mouse', 'lion', 'tiger', 'bear', 'wolf', 'fox', 'rabbit', 'horse', 'cow', 'pig', 'sheep'];
224
+ const animalWords = wordList.filter(w => animals.some(animal => w.includes(animal) || animal.includes(w)));
225
+ if (animalWords.length >= 2) {
226
+ themes.push(`🐾 Animals: ${animalWords.join(', ')}`);
227
+ }
228
+
229
+ // Look for nature words
230
+ const nature = ['tree', 'flower', 'water', 'sun', 'moon', 'star', 'cloud', 'rain', 'snow', 'mountain', 'ocean', 'forest'];
231
+ const natureWords = wordList.filter(w => nature.some(n => w.includes(n) || n.includes(w)));
232
+ if (natureWords.length >= 2) {
233
+ themes.push(`🌿 Nature: ${natureWords.join(', ')}`);
234
+ }
235
+
236
+ return themes;
237
+ }
238
+
239
+ module.exports = {
240
+ createInitialWordAssociationState,
241
+ addPlayer,
242
+ makeMove,
243
+ formatWordAssociationDisplay,
244
+ isValidWord,
245
+ getGameStats,
246
+ findThemes
247
+ };
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Word Chain game implementation for /vibe
3
+ * Players take turns saying words that start with the last letter of the previous word
4
+ */
5
+
6
+ // Simple word validation - checks if it's a reasonable English word
7
+ // In a full implementation, this could use a proper dictionary API
8
+ function isValidWord(word) {
9
+ // Basic checks: alphabetic, reasonable length, not too short
10
+ if (!/^[a-zA-Z]+$/.test(word)) return false;
11
+ if (word.length < 2) return false;
12
+ if (word.length > 20) return false;
13
+
14
+ // Some very basic filtering of common valid words vs nonsense
15
+ // This is a simplified check - a real game would use a dictionary
16
+ const commonPrefixes = ['th', 'he', 'in', 'er', 'an', 're', 'ed', 'nd', 'ou', 'ea', 'ti', 'to', 'it', 'st', 'io', 'le', 'is', 'on', 'ur', 'ar', 'nt', 'al'];
17
+ const commonSuffixes = ['ed', 'er', 'ing', 'ly', 'est', 'ion', 'tion', 'sion', 'ness', 'ment', 'able', 'ible'];
18
+
19
+ const lowerWord = word.toLowerCase();
20
+
21
+ // Allow single letters for now (like 'a', 'i')
22
+ if (word.length === 1) {
23
+ return ['a', 'i'].includes(lowerWord);
24
+ }
25
+
26
+ // Check for common patterns that suggest it's a real word
27
+ const hasCommonPrefix = commonPrefixes.some(prefix => lowerWord.startsWith(prefix));
28
+ const hasCommonSuffix = commonSuffixes.some(suffix => lowerWord.endsWith(suffix));
29
+
30
+ // Very permissive check - if it looks word-like, allow it
31
+ // Real implementation would check against dictionary
32
+ return hasCommonPrefix || hasCommonSuffix || lowerWord.length >= 3;
33
+ }
34
+
35
+ // Create initial word chain state
36
+ function createInitialWordChainState() {
37
+ return {
38
+ words: [],
39
+ currentPlayer: 'player1', // player1 starts (the person who initiated the game)
40
+ moves: 0,
41
+ gameOver: false,
42
+ winner: null,
43
+ lastLetter: null,
44
+ usedWords: new Set() // Track words to prevent repeats
45
+ };
46
+ }
47
+
48
+ // Make a word chain move
49
+ function makeMove(gameState, word, isPlayer1Move) {
50
+ const { words, currentPlayer, moves, usedWords, lastLetter } = gameState;
51
+
52
+ // Normalize word
53
+ const normalizedWord = word.toLowerCase().trim();
54
+
55
+ // Validate basic word format
56
+ if (!isValidWord(normalizedWord)) {
57
+ return { error: 'Invalid word format. Use only letters, 2-20 characters.' };
58
+ }
59
+
60
+ // Check if it's the right player's turn
61
+ const expectedPlayer = currentPlayer === 'player1' ? true : false;
62
+ if (isPlayer1Move !== expectedPlayer) {
63
+ return { error: 'Not your turn!' };
64
+ }
65
+
66
+ // Check if word was already used
67
+ if (usedWords.has(normalizedWord)) {
68
+ return { error: 'Word already used! Try a different word.' };
69
+ }
70
+
71
+ // Check if word starts with the required letter
72
+ if (lastLetter && normalizedWord[0] !== lastLetter) {
73
+ return { error: `Word must start with "${lastLetter.toUpperCase()}"` };
74
+ }
75
+
76
+ // Valid move - update game state
77
+ const newWords = [...words, normalizedWord];
78
+ const newUsedWords = new Set([...usedWords, normalizedWord]);
79
+ const newLastLetter = normalizedWord[normalizedWord.length - 1];
80
+ const newCurrentPlayer = currentPlayer === 'player1' ? 'player2' : 'player1';
81
+
82
+ const newGameState = {
83
+ words: newWords,
84
+ currentPlayer: newCurrentPlayer,
85
+ moves: moves + 1,
86
+ gameOver: false,
87
+ winner: null,
88
+ lastLetter: newLastLetter,
89
+ usedWords: newUsedWords,
90
+ lastWord: normalizedWord
91
+ };
92
+
93
+ return { success: true, gameState: newGameState };
94
+ }
95
+
96
+ // Format word chain for display
97
+ function formatWordChainDisplay(gameState) {
98
+ const { words, currentPlayer, moves, lastLetter, lastWord } = gameState;
99
+
100
+ let display = `🔗 **Word Chain** (${moves} words)\n\n`;
101
+
102
+ if (words.length === 0) {
103
+ display += '**Start the chain with any word!**\n\n';
104
+ display += 'Next: **Player 1**';
105
+ } else {
106
+ // Show the chain
107
+ display += '**Chain:** ' + words.map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' → ') + '\n\n';
108
+
109
+ if (lastLetter) {
110
+ display += `**Next word must start with: "${lastLetter.toUpperCase()}"**\n\n`;
111
+ }
112
+
113
+ display += `Turn: **${currentPlayer === 'player1' ? 'Player 1' : 'Player 2'}**`;
114
+
115
+ if (lastWord) {
116
+ display += `\nLast word: "${lastWord.charAt(0).toUpperCase() + lastWord.slice(1)}"`;
117
+ }
118
+ }
119
+
120
+ return display;
121
+ }
122
+
123
+ // Check if a letter is considered "difficult" (fewer common words start with it)
124
+ function isDifficultLetter(letter) {
125
+ const difficultLetters = ['x', 'z', 'q', 'j', 'v'];
126
+ return difficultLetters.includes(letter.toLowerCase());
127
+ }
128
+
129
+ module.exports = {
130
+ createInitialWordChainState,
131
+ makeMove,
132
+ formatWordChainDisplay,
133
+ isValidWord,
134
+ isDifficultLetter
135
+ };
package/index.js CHANGED
@@ -6,11 +6,18 @@
6
6
  * Identity, presence, DM. That's it.
7
7
  */
8
8
 
9
+ // Handle "setup" command - redirect to setup.js
10
+ if (process.argv.includes('setup')) {
11
+ require('./setup.js');
12
+ return;
13
+ }
14
+
9
15
  const presence = require('./presence');
10
16
  const config = require('./config');
11
17
  const store = require('./store');
12
18
  const prompts = require('./prompts');
13
19
  const NotificationEmitter = require('./notification-emitter');
20
+ const authStore = require('./auth-store');
14
21
 
15
22
  // Tools that shouldn't show presence footer (would be redundant/noisy)
16
23
  const SKIP_FOOTER_TOOLS = ['vibe_init', 'vibe_doctor', 'vibe_test', 'vibe_update', 'vibe_settings', 'vibe_notifications'];
@@ -36,9 +43,6 @@ function inferPromptFromArgs(toolName, args) {
36
43
  case 'context': return 'share context';
37
44
  case 'summarize': return 'summarize session';
38
45
  case 'bye': return 'end session';
39
- case 'remember': return `remember about ${handle}`;
40
- case 'recall': return `recall ${handle}`;
41
- case 'forget': return `forget ${handle}`;
42
46
  case 'board': return args.content ? 'post to board' : 'view board';
43
47
  case 'observe': return args.content ? 'record observation' : 'view observations';
44
48
  case 'invite': return 'generate invite';
@@ -50,19 +54,19 @@ function inferPromptFromArgs(toolName, args) {
50
54
  case 'release': return `release ${args.reservation_id || 'reservation'}`;
51
55
  case 'reservations': return 'list reservations';
52
56
  case 'solo_game': return `play ${args.game || 'game'}`;
53
- case 'tictactoe': return `play tic-tac-toe ${args.difficulty || ''}`.trim();
54
- case 'wordassociation': return args.word ? `word association: ${args.word}` : 'play word association';
57
+ case 'game': return `play ${args.game || 'game'} with ${handle}`;
55
58
  case 'multiplayer_game': return `multiplayer ${args.game || 'game'}`;
56
59
  case 'drawing': return args.action ? `drawing ${args.action}` : 'collaborative drawing';
57
60
  case 'crossword': return `crossword ${args.action || 'daily'}`;
58
- case 'away': return args.message ? `set away: "${args.message}"` : 'go away';
59
- case 'back': return 'come back';
60
61
  case 'discover': return `discover ${args.command || 'suggest'}`;
61
62
  case 'suggest_tags': return `suggest tags ${args.command || 'suggest'}`;
62
- case 'skills_exchange': return `skills exchange ${args.command || 'browse'}`;
63
- case 'workshop_buddy': return `workshop buddy ${args.command || 'find'}`;
63
+ case 'settings': return args.mute ? `mute for ${args.mute}` : 'view settings';
64
64
  case 'create_artifact': return `create ${args.template || 'artifact'}: ${args.title || 'untitled'}`;
65
65
  case 'view_artifact': return args.slug ? `view artifact ${args.slug}` : `list ${args.list || 'artifacts'}`;
66
+ case 'broadcast': return args.action ? `broadcast ${args.action}` : 'broadcast status';
67
+ case 'watch': return args.target ? `watch ${args.target}` : 'list live broadcasts';
68
+ case 'session': return args.action ? `session ${args.action}` : 'list sessions';
69
+ case 'chat': return args.message ? `chat "${args.message.slice(0, 30)}..."` : 'view chat';
66
70
  default: return `${action} ${handle}`.trim() || null;
67
71
  }
68
72
  }
@@ -164,45 +168,42 @@ async function getPresenceFooter() {
164
168
  }
165
169
  }
166
170
 
167
- // Load all tools
168
- const tools = {
171
+ // Load core tools (always available)
172
+ const coreTools = {
169
173
  // Entry point
170
174
  vibe_start: require('./tools/start'),
171
175
  // Core
172
176
  vibe_init: require('./tools/init'),
173
- vibe_token: require('./tools/token'), // Privy OAuth token setter
177
+ vibe_token: require('./tools/token'), // GitHub OAuth token setter
174
178
  vibe_migrate: require('./tools/migrate'), // Migrate existing handle to GitHub auth
175
179
  vibe_who: require('./tools/who'),
176
180
  vibe_ping: require('./tools/ping'),
177
181
  vibe_react: require('./tools/react'),
178
182
  vibe_dm: require('./tools/dm'),
183
+ vibe_reply: require('./tools/reply'), // Quick reply to most recent unread
179
184
  vibe_inbox: require('./tools/inbox'),
185
+ vibe_search_messages: require('./tools/search-messages'), // Search DM history
186
+ vibe_connection_status: require('./tools/connection-status'), // Check connection health
187
+ vibe_smart_check: require('./tools/smart-check'), // Smart inbox check at natural breaks
180
188
  vibe_open: require('./tools/open'),
181
189
  vibe_status: require('./tools/status'),
182
190
  vibe_context: require('./tools/context'),
191
+ vibe_work_summary: require('./tools/work-summary'), // Ambient context for composing messages
183
192
  vibe_summarize: require('./tools/summarize'),
184
193
  vibe_bye: require('./tools/bye'),
185
194
  vibe_game: require('./tools/game'),
186
195
  vibe_solo_game: require('./tools/solo-game'),
187
196
  vibe_party_game: require('./tools/party-game'),
188
- vibe_tictactoe: require('./tools/tictactoe'),
189
- vibe_wordassociation: require('./tools/wordassociation'),
190
197
  vibe_multiplayer_game: require('./tools/multiplayer-game'),
191
198
  vibe_drawing: require('./tools/drawing'),
192
- // Nostalgia Stack
193
199
  vibe_crossword: require('./tools/crossword'),
194
- vibe_away: require('./tools/away'),
195
- vibe_back: require('./tools/back'),
196
200
  // AIRC Handoff (v1) — context portability
197
201
  vibe_handoff: require('./tools/handoff'),
198
202
  // File reservations (advisory locks)
199
203
  vibe_reserve: require('./tools/reserve'),
200
204
  vibe_release: require('./tools/release'),
201
205
  vibe_reservations: require('./tools/reservations'),
202
- // Memory tools (Tier 1 Collaborative Memory)
203
- vibe_remember: require('./tools/remember'),
204
- vibe_recall: require('./tools/recall'),
205
- vibe_forget: require('./tools/forget'),
206
+ // Memory tools removedsimplified UX
206
207
  // Consent (AIRC compliance)
207
208
  vibe_consent: require('./tools/consent'),
208
209
  // Trust & Safety
@@ -220,20 +221,25 @@ const tools = {
220
221
  vibe_ship: require('./tools/ship'),
221
222
  vibe_feed: require('./tools/feed'),
222
223
  vibe_insights: require('./tools/insights'),
224
+ // Launch Analytics — Real-time metrics dashboard
225
+ vibe_analytics: require('./tools/analytics'),
226
+ // Activity Feed — Real-time community activity
227
+ vibe_activity: require('./tools/activity'),
223
228
  // Artifacts — Just-in-time social objects
224
229
  vibe_create_artifact: require('./tools/artifact-create'),
225
230
  vibe_view_artifact: require('./tools/artifact-view'),
231
+ // Plans — Async approval workflow
232
+ vibe_plan: require('./tools/plan'),
233
+ vibe_approve: require('./tools/approve'),
226
234
  // Discovery & Matchmaking
227
235
  vibe_discover: require('./tools/discover'),
228
236
  vibe_suggest_tags: require('./tools/suggest-tags'),
229
- vibe_skills_exchange: require('./tools/skills-exchange'),
230
- vibe_workshop_buddy: require('./tools/workshop-buddy'),
231
- // Admin tools
232
- vibe_admin_inbox: require('./tools/admin-inbox'),
233
- // Diagnostics
234
- vibe_test: require('./tools/test'),
235
- vibe_doctor: require('./tools/doctor'),
236
- vibe_update: require('./tools/update'),
237
+ // Follow system
238
+ vibe_follow: require('./tools/follow'),
239
+ // GitHub friends import (Plaxo move)
240
+ vibe_friends: require('./tools/friends'),
241
+ // Scheduling
242
+ vibe_schedule: require('./tools/schedule'),
237
243
  // @echo feedback agent (by Flynn)
238
244
  vibe_echo: require('./tools/echo'),
239
245
  // X/Twitter bridge
@@ -242,29 +248,62 @@ const tools = {
242
248
  // Unified social inbox (Phase 1a)
243
249
  vibe_social_inbox: require('./tools/social-inbox'),
244
250
  vibe_social_post: require('./tools/social-post'),
245
- // Language evolution
246
- vibe_patterns: require('./tools/patterns'),
247
251
  // Settings
248
252
  vibe_settings: require('./tools/settings'),
249
253
  // External notification channels
250
254
  vibe_notifications: require('./tools/notifications'),
251
255
  // Background presence agent (Claude Code 2.1)
252
256
  vibe_presence_agent: require('./tools/presence-agent'),
253
- vibe_mute: require('./tools/mute'),
254
257
  // Onboarding checklist
255
258
  vibe_onboarding: require('./tools/onboarding'),
256
259
  // VIBE L2 tools
257
260
  vibe_l2: require('./tools/l2'),
258
261
  vibe_l2_status: require('./tools/l2-status'),
259
262
  vibe_shipback: require('./tools/shipback'),
260
- vibe_bridge: require('./tools/l2-bridge')
263
+ vibe_bridge: require('./tools/l2-bridge'),
264
+ // Gig Marketplace & Proof-of-Work
265
+ vibe_gig_browse: require('./tools/gig-browse'),
266
+ vibe_gig_complete: require('./tools/gig-complete'),
267
+ vibe_proof_of_work: require('./tools/proof-of-work'),
268
+ vibe_artifacts_price: require('./tools/artifacts-price'),
269
+ // Watch Me Code — Live broadcasting & sessions
270
+ vibe_broadcast: require('./tools/broadcast'),
271
+ vibe_watch: require('./tools/watch'),
272
+ vibe_session: require('./tools/session'),
273
+ vibe_chat: require('./tools/chat'),
274
+ // Help Signal — Symbiosis layer
275
+ vibe_stuck: require('./tools/stuck'),
276
+ // Availability — Signal openness to chat
277
+ vibe_available: require('./tools/available'),
278
+ // VIBE Economy — Tips, Earnings, Withdrawals, Subscriptions
279
+ vibe_tip: require('./tools/tip'),
280
+ vibe_earnings: require('./tools/earnings'),
281
+ vibe_withdraw: require('./tools/withdraw'),
282
+ vibe_session_price: require('./tools/session_price'),
283
+ vibe_subscribe: require('./tools/subscribe'),
284
+ vibe_subscriptions: require('./tools/subscriptions')
261
285
  };
262
286
 
287
+ // Admin tools (only loaded when VIBE_ADMIN=true)
288
+ const adminTools = process.env.VIBE_ADMIN === 'true' ? {
289
+ vibe_admin_inbox: require('./tools/admin-inbox'),
290
+ vibe_test: require('./tools/test'),
291
+ vibe_doctor: require('./tools/doctor'),
292
+ vibe_update: require('./tools/update'),
293
+ vibe_patterns: require('./tools/patterns'),
294
+ } : {};
295
+
296
+ // Combine tools
297
+ const tools = { ...coreTools, ...adminTools };
298
+
263
299
  /**
264
300
  * MCP Protocol Handler
265
301
  */
266
302
  class VibeMCPServer {
267
303
  constructor() {
304
+ // Hydrate auth state from disk FIRST (before any tools need it)
305
+ authStore.hydrate();
306
+
268
307
  // Initialize notification emitter
269
308
  this.notifier = new NotificationEmitter(this);
270
309
 
@@ -345,9 +384,10 @@ class VibeMCPServer {
345
384
  // Emit list_changed notification for state-changing tools
346
385
  // This triggers Claude to refresh without reconnection
347
386
  const stateChangingTools = [
348
- 'vibe_dm', 'vibe_ping', 'vibe_react', 'vibe_remember',
387
+ 'vibe_dm', 'vibe_ping', 'vibe_react',
349
388
  'vibe_status', 'vibe_context', 'vibe_handoff',
350
- 'vibe_reserve', 'vibe_release'
389
+ 'vibe_reserve', 'vibe_release',
390
+ 'vibe_broadcast', 'vibe_session', 'vibe_chat'
351
391
  ];
352
392
  if (stateChangingTools.includes(params.name)) {
353
393
  // Debounced notification (prevents spam)
@@ -360,29 +400,13 @@ class VibeMCPServer {
360
400
  footer = await getPresenceFooter();
361
401
  }
362
402
 
363
- // Build simplified hint indicator for Claude (human-readable)
364
- let hintIndicator = '';
365
- if (result.hint) {
366
- // Simple format: <!-- vibe: hint_type @handle (count) -->
367
- const hint = result.hint;
368
- const handle = result.suggestion?.handle || result.for_handle || '';
369
- const count = result.unread_count || '';
370
-
371
- // Build minimal hint string
372
- let hintParts = [hint];
373
- if (handle) hintParts.push(`@${handle.replace('@', '')}`);
374
- if (count) hintParts.push(`(${count})`);
375
-
376
- hintIndicator = `\n\n<!-- vibe: ${hintParts.join(' ')} -->`;
377
- }
378
-
379
403
  return {
380
404
  jsonrpc: '2.0',
381
405
  id,
382
406
  result: {
383
407
  content: [{
384
408
  type: 'text',
385
- text: (result.display || JSON.stringify(result, null, 2)) + hintIndicator + footer
409
+ text: (result.display || JSON.stringify(result, null, 2)) + footer
386
410
  }]
387
411
  }
388
412
  };
@@ -443,7 +467,7 @@ class VibeMCPServer {
443
467
 
444
468
  async checkForUpdates() {
445
469
  try {
446
- const { checkForUpdates, formatUpdateNotification } = await import('./auto-update.js');
470
+ const { checkForUpdates, formatUpdateNotification } = require('./auto-update');
447
471
  const update = await checkForUpdates();
448
472
 
449
473
  if (update) {
@@ -1,17 +1,19 @@
1
1
  /**
2
2
  * Intelligence Module — Ambient Social Awareness
3
3
  *
4
- * Four layers of intelligence:
4
+ * Five layers of intelligence:
5
5
  * 1. Infer — Smart state detection from context signals
6
6
  * 2. Serendipity — Surface meaningful coincidences
7
7
  * 3. Proactive — Background agent for social moments
8
8
  * 4. Patterns — Persistent behavioral memory (work, social, creative)
9
+ * 5. Interests — Live interest inference from context (Phase 2)
9
10
  */
10
11
 
11
12
  const infer = require('./infer');
12
13
  const serendipity = require('./serendipity');
13
14
  const proactive = require('./proactive');
14
15
  const patterns = require('./patterns');
16
+ const interests = require('./interests');
15
17
 
16
18
  module.exports = {
17
19
  // Inference
@@ -34,5 +36,10 @@ module.exports = {
34
36
  setSessionStart: proactive.setSessionStart,
35
37
 
36
38
  // Patterns (persistent behavioral memory)
37
- patterns
39
+ patterns,
40
+
41
+ // Interests (live context inference - Phase 2)
42
+ inferLiveInterests: interests.inferLiveInterests,
43
+ mergeInterests: interests.mergeInterests,
44
+ formatLiveInterests: interests.formatLiveInterests
38
45
  };