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
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Smart Interest & Tag Suggestions — Help users discover what to tag themselves with
3
+ *
4
+ * Analyzes what someone is building and suggests:
5
+ * - Interest tags (broad categories: ai, fintech, gaming)
6
+ * - Skill tags (specific tech: react, python, figma)
7
+ * - Discovery hints (who else to connect with)
8
+ */
9
+
10
+ const userProfiles = require('../store/profiles');
11
+
12
+ // Tech stack detection patterns
13
+ const TECH_PATTERNS = {
14
+ // Frontend
15
+ 'react': ['react', 'jsx', 'create-react-app', 'react native'],
16
+ 'vue': ['vue', 'vuejs', 'vue.js'],
17
+ 'angular': ['angular', 'ng', '@angular'],
18
+ 'svelte': ['svelte', 'sveltekit'],
19
+ 'typescript': ['typescript', 'ts', '.ts', 'tsc'],
20
+ 'javascript': ['javascript', 'js', 'node', 'npm'],
21
+
22
+ // Backend
23
+ 'python': ['python', 'django', 'flask', 'fastapi', 'py'],
24
+ 'node': ['node', 'nodejs', 'express', 'koa'],
25
+ 'go': ['golang', 'go', 'gin', 'fiber'],
26
+ 'rust': ['rust', 'cargo', 'actix', 'warp'],
27
+ 'java': ['java', 'spring', 'maven', 'gradle'],
28
+
29
+ // Data & AI
30
+ 'ai': ['ai', 'llm', 'gpt', 'claude', 'openai', 'machine learning', 'ml'],
31
+ 'data': ['data', 'pandas', 'numpy', 'jupyter', 'sql', 'database'],
32
+ 'ml': ['tensorflow', 'pytorch', 'scikit', 'keras', 'neural'],
33
+
34
+ // Mobile
35
+ 'ios': ['ios', 'swift', 'swiftui', 'xcode', 'iphone', 'ipad'],
36
+ 'android': ['android', 'kotlin', 'java android', 'play store'],
37
+ 'flutter': ['flutter', 'dart'],
38
+ 'react-native': ['react native', 'expo', 'react-native'],
39
+
40
+ // Design
41
+ 'design': ['figma', 'sketch', 'adobe', 'ui', 'ux', 'design'],
42
+ 'frontend': ['css', 'html', 'sass', 'tailwind', 'bootstrap'],
43
+
44
+ // Infrastructure
45
+ 'devops': ['docker', 'kubernetes', 'aws', 'gcp', 'azure', 'terraform'],
46
+ 'database': ['postgres', 'mysql', 'mongodb', 'redis', 'sqlite'],
47
+
48
+ // Domains
49
+ 'crypto': ['crypto', 'blockchain', 'web3', 'ethereum', 'bitcoin', 'defi'],
50
+ 'gaming': ['game', 'unity', 'unreal', 'godot', 'gamedev'],
51
+ 'fintech': ['fintech', 'payments', 'banking', 'trading', 'finance'],
52
+ 'healthcare': ['health', 'medical', 'biotech', 'pharma'],
53
+ 'ecommerce': ['ecommerce', 'shopify', 'stripe', 'payment'],
54
+ 'saas': ['saas', 'b2b', 'enterprise', 'subscription'],
55
+ 'social': ['social', 'community', 'chat', 'messaging'],
56
+ 'productivity': ['productivity', 'workflow', 'automation', 'tool']
57
+ };
58
+
59
+ // Interest categories (broader themes)
60
+ const INTEREST_PATTERNS = {
61
+ 'startups': ['startup', 'founder', 'entrepreneurship', 'indie hacker', 'side project'],
62
+ 'open source': ['open source', 'oss', 'github', 'contribute', 'maintainer'],
63
+ 'developer tools': ['dev tool', 'cli', 'vscode', 'editor', 'developer experience'],
64
+ 'automation': ['automation', 'workflow', 'scripting', 'no-code', 'zapier'],
65
+ 'content creation': ['content', 'blog', 'writing', 'video', 'podcast', 'creator'],
66
+ 'education': ['education', 'learning', 'teaching', 'course', 'tutorial'],
67
+ 'remote work': ['remote', 'distributed', 'async', 'nomad', 'wfh'],
68
+ 'indie': ['indie', 'bootstrap', 'solo', 'maker', 'build in public']
69
+ };
70
+
71
+ // Analyze text and suggest tags
72
+ function suggestTags(buildingText) {
73
+ if (!buildingText) return { tech: [], interests: [], confidence: 0 };
74
+
75
+ const text = buildingText.toLowerCase();
76
+ const suggestions = {
77
+ tech: new Set(),
78
+ interests: new Set(),
79
+ matches: []
80
+ };
81
+
82
+ // Find tech matches
83
+ for (const [tag, patterns] of Object.entries(TECH_PATTERNS)) {
84
+ for (const pattern of patterns) {
85
+ if (text.includes(pattern.toLowerCase())) {
86
+ suggestions.tech.add(tag);
87
+ suggestions.matches.push({ tag, pattern, type: 'tech' });
88
+ break; // Only count once per tag
89
+ }
90
+ }
91
+ }
92
+
93
+ // Find interest matches
94
+ for (const [interest, patterns] of Object.entries(INTEREST_PATTERNS)) {
95
+ for (const pattern of patterns) {
96
+ if (text.includes(pattern.toLowerCase())) {
97
+ suggestions.interests.add(interest);
98
+ suggestions.matches.push({ tag: interest, pattern, type: 'interest' });
99
+ break;
100
+ }
101
+ }
102
+ }
103
+
104
+ // Calculate confidence based on number of matches
105
+ const totalMatches = suggestions.matches.length;
106
+ const confidence = Math.min(totalMatches * 20, 100); // Cap at 100%
107
+
108
+ return {
109
+ tech: Array.from(suggestions.tech).slice(0, 5), // Limit suggestions
110
+ interests: Array.from(suggestions.interests).slice(0, 3),
111
+ confidence,
112
+ matches: suggestions.matches
113
+ };
114
+ }
115
+
116
+ // Find similar builders based on tags
117
+ async function findSimilarBuilders(suggestedTags, limit = 3) {
118
+ const allProfiles = await userProfiles.getAllProfiles();
119
+ const matches = [];
120
+
121
+ for (const profile of allProfiles) {
122
+ if (!profile.tags && !profile.interests) continue;
123
+
124
+ let overlap = 0;
125
+ const overlappingTags = [];
126
+
127
+ // Check tech tag overlap
128
+ if (profile.tags) {
129
+ for (const tag of suggestedTags.tech) {
130
+ if (profile.tags.includes(tag)) {
131
+ overlap += 2; // Tech overlap is worth more
132
+ overlappingTags.push(tag);
133
+ }
134
+ }
135
+ }
136
+
137
+ // Check interest overlap
138
+ if (profile.interests) {
139
+ for (const interest of suggestedTags.interests) {
140
+ if (profile.interests.includes(interest)) {
141
+ overlap += 1;
142
+ overlappingTags.push(interest);
143
+ }
144
+ }
145
+ }
146
+
147
+ if (overlap > 0) {
148
+ matches.push({
149
+ handle: profile.handle,
150
+ overlap,
151
+ overlappingTags,
152
+ building: profile.building,
153
+ lastSeen: profile.lastSeen
154
+ });
155
+ }
156
+ }
157
+
158
+ return matches
159
+ .sort((a, b) => b.overlap - a.overlap)
160
+ .slice(0, limit);
161
+ }
162
+
163
+ // Generate tag suggestions for a user
164
+ async function generateSuggestions(handle) {
165
+ const profile = await userProfiles.getProfile(handle);
166
+
167
+ if (!profile.building) {
168
+ return {
169
+ error: 'No building info found. Set what you\'re building first:\n`vibe update building "your project description"`'
170
+ };
171
+ }
172
+
173
+ const suggestions = suggestTags(profile.building);
174
+ const similarBuilders = await findSimilarBuilders(suggestions);
175
+
176
+ // Filter out already set tags
177
+ const currentTags = new Set([...(profile.tags || []), ...(profile.interests || [])]);
178
+ const newTechTags = suggestions.tech.filter(t => !currentTags.has(t));
179
+ const newInterests = suggestions.interests.filter(i => !currentTags.has(i));
180
+
181
+ return {
182
+ building: profile.building,
183
+ confidence: suggestions.confidence,
184
+ currentTags: profile.tags || [],
185
+ currentInterests: profile.interests || [],
186
+ suggested: {
187
+ tech: newTechTags,
188
+ interests: newInterests
189
+ },
190
+ similarBuilders,
191
+ matches: suggestions.matches
192
+ };
193
+ }
194
+
195
+ // Get trending tags across all users
196
+ async function getTrendingTags() {
197
+ const allTags = await userProfiles.getTrendingTags();
198
+ const allInterests = await userProfiles.getTrendingInterests();
199
+
200
+ return {
201
+ popularTags: allTags.slice(0, 10),
202
+ popularInterests: allInterests.slice(0, 8)
203
+ };
204
+ }
205
+
206
+ // Smart tag completion - suggest similar tags based on partial input
207
+ function suggestSimilarTags(partial) {
208
+ const allTags = Object.keys(TECH_PATTERNS);
209
+ const allInterests = Object.keys(INTEREST_PATTERNS);
210
+
211
+ const search = partial.toLowerCase();
212
+ const matches = [];
213
+
214
+ // Exact prefix matches first
215
+ for (const tag of [...allTags, ...allInterests]) {
216
+ if (tag.startsWith(search)) {
217
+ matches.push({ tag, type: allTags.includes(tag) ? 'tech' : 'interest', score: 3 });
218
+ }
219
+ }
220
+
221
+ // Substring matches second
222
+ for (const tag of [...allTags, ...allInterests]) {
223
+ if (tag.includes(search) && !tag.startsWith(search)) {
224
+ matches.push({ tag, type: allTags.includes(tag) ? 'tech' : 'interest', score: 2 });
225
+ }
226
+ }
227
+
228
+ // Pattern matches third (e.g., "react" matches "react native")
229
+ for (const [tag, patterns] of [...Object.entries(TECH_PATTERNS), ...Object.entries(INTEREST_PATTERNS)]) {
230
+ for (const pattern of patterns) {
231
+ if (pattern.includes(search) && !tag.includes(search)) {
232
+ matches.push({
233
+ tag,
234
+ type: Object.keys(TECH_PATTERNS).includes(tag) ? 'tech' : 'interest',
235
+ score: 1,
236
+ matchedPattern: pattern
237
+ });
238
+ break;
239
+ }
240
+ }
241
+ }
242
+
243
+ return matches
244
+ .sort((a, b) => b.score - a.score)
245
+ .slice(0, 8)
246
+ .map(m => m.tag);
247
+ }
248
+
249
+ module.exports = {
250
+ suggestTags,
251
+ generateSuggestions,
252
+ findSimilarBuilders,
253
+ getTrendingTags,
254
+ suggestSimilarTags,
255
+ TECH_PATTERNS,
256
+ INTEREST_PATTERNS
257
+ };
package/tools/tip.js ADDED
@@ -0,0 +1,193 @@
1
+ /**
2
+ * vibe_tip - Send an instant on-chain USDC tip
3
+ *
4
+ * Real crypto tips with instant settlement on Base Sepolia.
5
+ * No fees - creator gets 100% of the tip.
6
+ * Gas is sponsored by /vibe.
7
+ *
8
+ * Examples:
9
+ * - "tip @alice $5 for helping debug"
10
+ * - "send @bob $1 thanks for the intro"
11
+ * - "vibe tip @charlie 10"
12
+ *
13
+ * Preset amounts: $1 (default), $5, $10
14
+ */
15
+
16
+ const config = require('../config');
17
+ const { requireInit, normalizeHandle, displayHandle } = require('./_shared');
18
+ const { actions, formatActions } = require('./_actions');
19
+
20
+ // Preset tip amounts in cents
21
+ const TIP_PRESETS = {
22
+ 1: 100, // $1
23
+ 5: 500, // $5
24
+ 10: 1000 // $10
25
+ };
26
+
27
+ const definition = {
28
+ name: 'vibe_tip',
29
+ description: 'Send an instant on-chain USDC tip. 0% fee - creator gets 100%. Gas is sponsored. Amounts: $1 (default), $5, $10.',
30
+ inputSchema: {
31
+ type: 'object',
32
+ properties: {
33
+ to: {
34
+ type: 'string',
35
+ description: 'Recipient handle (e.g., @alice)'
36
+ },
37
+ amount: {
38
+ type: 'number',
39
+ description: 'Amount in USD (1, 5, or 10). Default: 1'
40
+ },
41
+ message: {
42
+ type: 'string',
43
+ description: 'Optional message to include with the tip'
44
+ },
45
+ context: {
46
+ type: 'object',
47
+ description: 'Optional context (type: dm_reply|ship|profile|gig_bonus, reference_id)',
48
+ properties: {
49
+ type: {
50
+ type: 'string',
51
+ enum: ['dm_reply', 'ship', 'profile', 'gig_bonus']
52
+ },
53
+ reference_id: {
54
+ type: 'string'
55
+ }
56
+ }
57
+ }
58
+ },
59
+ required: ['to']
60
+ }
61
+ };
62
+
63
+ async function handler(args) {
64
+ const initCheck = requireInit();
65
+ if (initCheck) return initCheck;
66
+
67
+ const { to, message, context } = args;
68
+ let { amount = 1 } = args;
69
+
70
+ const myHandle = config.getHandle();
71
+ const token = config.getToken();
72
+
73
+ if (!token) {
74
+ return {
75
+ display: 'āŒ **Not authenticated**\n\nRun `vibe init` to authenticate with GitHub first.'
76
+ };
77
+ }
78
+
79
+ // Normalize recipient handle
80
+ const recipient = normalizeHandle(to);
81
+ if (!recipient) {
82
+ return {
83
+ display: 'āŒ **Missing recipient**\n\nPlease specify who to tip (e.g., `tip @alice 5`).'
84
+ };
85
+ }
86
+
87
+ // Validate and normalize amount to preset
88
+ amount = parseFloat(amount);
89
+ if (isNaN(amount) || amount <= 0) {
90
+ amount = 1;
91
+ }
92
+
93
+ // Round to nearest preset
94
+ let amountCents;
95
+ if (amount >= 7.5) {
96
+ amountCents = TIP_PRESETS[10];
97
+ amount = 10;
98
+ } else if (amount >= 3) {
99
+ amountCents = TIP_PRESETS[5];
100
+ amount = 5;
101
+ } else {
102
+ amountCents = TIP_PRESETS[1];
103
+ amount = 1;
104
+ }
105
+
106
+ // Call instant tip API
107
+ const apiUrl = config.getApiUrl();
108
+
109
+ // Generate idempotency key to prevent duplicate tips from retries
110
+ // Key is based on: sender + recipient + amount + timestamp (1-minute bucket)
111
+ const timeBucket = Math.floor(Date.now() / 60000); // 1-minute buckets
112
+ const idempotencyKey = `${myHandle}:${recipient}:${amountCents}:${timeBucket}`;
113
+
114
+ try {
115
+ const response = await fetch(`${apiUrl}/api/tips/instant`, {
116
+ method: 'POST',
117
+ headers: {
118
+ 'Content-Type': 'application/json',
119
+ 'Authorization': `Bearer ${token}`,
120
+ 'Idempotency-Key': idempotencyKey
121
+ },
122
+ body: JSON.stringify({
123
+ to: recipient,
124
+ amount_cents: amountCents,
125
+ message,
126
+ context
127
+ })
128
+ });
129
+
130
+ const result = await response.json();
131
+
132
+ if (!response.ok) {
133
+ // Handle specific error cases
134
+ if (result.error === 'insufficient_balance') {
135
+ return {
136
+ display: `āŒ **Insufficient USDC balance**\n\nYou need USDC to tip. Your balance: $${result.balance_usdc}\n\n` +
137
+ `Get USDC at [bridge.base.org](${result.help_url})`
138
+ };
139
+ }
140
+
141
+ if (result.error === 'recipient_no_wallet') {
142
+ return {
143
+ display: `āŒ **${displayHandle(recipient)} can't receive tips yet**\n\n` +
144
+ `They need to complete their wallet setup first.`
145
+ };
146
+ }
147
+
148
+ if (result.error === 'rate_limited') {
149
+ return {
150
+ display: `ā³ **Rate limit reached**\n\n${result.message}\n\nTry again in ${Math.ceil(result.reset_in_seconds / 60)} minutes.`
151
+ };
152
+ }
153
+
154
+ return {
155
+ display: `āŒ **Tip failed**\n\n${result.message || 'Please try again.'}`
156
+ };
157
+ }
158
+
159
+ // Success!
160
+ let display = `āœ… **Tipped ${displayHandle(recipient)} $${amount} USDC**\n\n`;
161
+
162
+ display += `šŸ’° Amount: $${amount}.00\n`;
163
+ display += `šŸ“¤ To: ${displayHandle(recipient)}\n`;
164
+ display += `šŸ“„ Fee: $0.00 (0%)\n`;
165
+ display += `✨ Net to creator: $${amount}.00\n`;
166
+
167
+ if (message) {
168
+ display += `\nšŸ’¬ _"${message}"_\n`;
169
+ }
170
+
171
+ display += `\nšŸ”— [View on Explorer](${result.explorer_url})`;
172
+
173
+ // Build response with actions
174
+ const response_obj = {
175
+ display,
176
+ tx_hash: result.tx_hash,
177
+ explorer_url: result.explorer_url
178
+ };
179
+
180
+ // Add follow-up actions
181
+ response_obj.actions = formatActions(actions.afterTip ? actions.afterTip(recipient) : actions.afterDm(recipient));
182
+
183
+ return response_obj;
184
+
185
+ } catch (error) {
186
+ console.error('[tip] Error:', error);
187
+ return {
188
+ display: `āŒ **Tip failed**\n\nError: ${error.message}\n\nPlease try again.`
189
+ };
190
+ }
191
+ }
192
+
193
+ module.exports = { definition, handler };
package/tools/token.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * vibe token — Set auth token after GitHub OAuth
3
+ *
4
+ * Usage:
5
+ * vibe token <paste-token-here>
6
+ *
7
+ * After authenticating via GitHub in browser, paste the token here
8
+ * to complete authentication.
9
+ */
10
+
11
+ const config = require('../config');
12
+ const store = require('../store');
13
+
14
+ const definition = {
15
+ name: 'vibe_token',
16
+ description: 'Set your auth token after browser authentication. Use after completing GitHub OAuth in browser.',
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ token: {
21
+ type: 'string',
22
+ description: 'The auth token from browser authentication'
23
+ }
24
+ },
25
+ required: ['token']
26
+ }
27
+ };
28
+
29
+ async function handler(args) {
30
+ const { token } = args;
31
+
32
+ if (!token || token.trim().length < 10) {
33
+ return {
34
+ display: `āŒ **Invalid token**
35
+
36
+ Please paste the full token from your browser authentication.
37
+
38
+ If you haven't authenticated yet:
39
+ 1. Run \`vibe init @yourhandle\` to start
40
+ 2. Open the auth URL in your browser
41
+ 3. Complete GitHub authentication
42
+ 4. Paste the token here`
43
+ };
44
+ }
45
+
46
+ // Verify token with server
47
+ const verification = await store.verifyAuthToken(token.trim());
48
+
49
+ if (!verification.valid) {
50
+ return {
51
+ display: `āŒ **Token verification failed**
52
+
53
+ Error: ${verification.error}
54
+
55
+ The token may be expired or invalid. Try authenticating again:
56
+ 1. Visit: ${config.getApiUrl()}/api/auth/github?handle=${config.getHandle() || 'yourhandle'}
57
+ 2. Complete GitHub authentication
58
+ 3. Paste the new token here`
59
+ };
60
+ }
61
+
62
+ // Save token
63
+ config.saveAuthToken(token.trim());
64
+
65
+ // Update session identity with verified handle
66
+ const handle = verification.handle;
67
+ const oneLiner = config.getOneLiner() || 'Building something';
68
+ config.setSessionIdentity(handle, oneLiner);
69
+
70
+ // Update shared config
71
+ const cfg = config.load();
72
+ cfg.handle = handle;
73
+ cfg.one_liner = oneLiner;
74
+ config.save(cfg);
75
+
76
+ // Remove old keypair (security improvement)
77
+ if (config.hasKeypair()) {
78
+ config.removeKeypair();
79
+ }
80
+
81
+ // Send initial heartbeat
82
+ await store.heartbeat(handle, oneLiner);
83
+
84
+ return {
85
+ display: `āœ… **Authenticated as @${handle}**
86
+
87
+ GitHub: @${verification.github || 'linked'}
88
+ Expires: ${verification.expiresAt ? new Date(verification.expiresAt * 1000).toLocaleString() : 'unknown'}
89
+
90
+ Your messages are now signed server-side (more secure).
91
+ ${config.hasKeypair() ? '' : 'šŸ”’ Old local keys have been removed.'}
92
+
93
+ You're ready to vibe! Try:
94
+ • \`vibe who\` — See who's online
95
+ • \`vibe inbox\` — Check messages
96
+ • \`vibe dm @someone "hello"\` — Send a message`,
97
+ handle,
98
+ github: verification.github,
99
+ authMethod: 'github'
100
+ };
101
+ }
102
+
103
+ module.exports = { definition, handler };
package/tools/update.js CHANGED
@@ -49,7 +49,7 @@ async function handler(args) {
49
49
  display += `Your install doesn't support automatic updates.\n\n`;
50
50
  display += `To update, re-run the installer:\n`;
51
51
  display += `\`\`\`\n`;
52
- display += `curl -fsSL https://raw.githubusercontent.com/brightseth/vibe/main/install.sh | bash\n`;
52
+ display += `curl -fsSL https://raw.githubusercontent.com/VibeCodingInc/vibe-mcp/main/install.sh | bash\n`;
53
53
  display += `\`\`\`\n\n`;
54
54
  display += `This will migrate you to the git-based installer for future updates.\n\n`;
55
55
  display += `---\n`;