slashvibe-mcp 0.2.9 → 0.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +1 -0
  2. package/analytics.js +107 -0
  3. package/auth-store.js +148 -0
  4. package/auto-update.js +130 -0
  5. package/bridges/bridge-monitor.js +388 -0
  6. package/bridges/discord-bot.js +431 -0
  7. package/bridges/farcaster.js +299 -0
  8. package/bridges/telegram.js +261 -0
  9. package/bridges/webhook-health.js +420 -0
  10. package/bridges/webhook-server.js +437 -0
  11. package/bridges/whatsapp.js +441 -0
  12. package/bridges/x-webhook.js +423 -0
  13. package/config.js +154 -5
  14. package/crypto.js +3 -8
  15. package/games/arcade.js +406 -0
  16. package/games/chess.js +451 -0
  17. package/games/colorguess.js +343 -0
  18. package/games/crossword-words.js +171 -0
  19. package/games/crossword.js +461 -0
  20. package/games/drawing.js +347 -0
  21. package/games/gameroulette.js +300 -0
  22. package/games/gamerouter.js +336 -0
  23. package/games/gamestatus.js +337 -0
  24. package/games/guessnumber.js +209 -0
  25. package/games/hangman.js +279 -0
  26. package/games/memory.js +338 -0
  27. package/games/multiplayer-tictactoe.js +389 -0
  28. package/games/pixelart.js +399 -0
  29. package/games/quickduel.js +354 -0
  30. package/games/riddle.js +371 -0
  31. package/games/rockpaperscissors.js +291 -0
  32. package/games/snake.js +406 -0
  33. package/games/storybuilder.js +343 -0
  34. package/games/tictactoe.js +345 -0
  35. package/games/twentyquestions.js +286 -0
  36. package/games/twotruths.js +207 -0
  37. package/games/werewolf.js +508 -0
  38. package/games/wordassociation.js +247 -0
  39. package/games/wordchain.js +135 -0
  40. package/index.js +105 -216
  41. package/intelligence/index.js +45 -0
  42. package/intelligence/infer.js +316 -0
  43. package/intelligence/interests.js +369 -0
  44. package/intelligence/patterns.js +651 -0
  45. package/intelligence/proactive.js +358 -0
  46. package/intelligence/serendipity.js +306 -0
  47. package/memory.js +166 -0
  48. package/notification-emitter.js +77 -0
  49. package/notify.js +102 -1
  50. package/package.json +21 -7
  51. package/prompts.js +1 -1
  52. package/protocol/index.js +161 -1
  53. package/setup.js +402 -0
  54. package/store/api.js +528 -82
  55. package/store/profiles.js +160 -12
  56. package/tools/_actions.js +463 -16
  57. package/tools/_deprecated/auto-suggest-connections.js +304 -0
  58. package/tools/_deprecated/bootstrap-skills.js +231 -0
  59. package/tools/_deprecated/bridge-dashboard.js +342 -0
  60. package/tools/_deprecated/bridge-health.js +400 -0
  61. package/tools/_deprecated/bridge-live.js +384 -0
  62. package/tools/_deprecated/bridges.js +383 -0
  63. package/tools/_deprecated/colorguess.js +281 -0
  64. package/tools/_deprecated/discover-insights.js +379 -0
  65. package/tools/_deprecated/discover-momentum.js +256 -0
  66. package/tools/_deprecated/discovery-analytics.js +345 -0
  67. package/tools/_deprecated/discovery-auto-suggest.js +275 -0
  68. package/tools/_deprecated/discovery-bootstrap.js +267 -0
  69. package/tools/_deprecated/discovery-daily.js +375 -0
  70. package/tools/_deprecated/discovery-dashboard.js +385 -0
  71. package/tools/_deprecated/discovery-digest.js +314 -0
  72. package/tools/_deprecated/discovery-hub.js +357 -0
  73. package/tools/_deprecated/discovery-insights.js +384 -0
  74. package/tools/_deprecated/discovery-momentum.js +281 -0
  75. package/tools/_deprecated/discovery-monitor.js +319 -0
  76. package/tools/_deprecated/discovery-proactive.js +300 -0
  77. package/tools/_deprecated/draw.js +317 -0
  78. package/tools/_deprecated/farcaster.js +307 -0
  79. package/tools/_deprecated/forget.js +119 -0
  80. package/tools/_deprecated/games-catalog.js +376 -0
  81. package/tools/_deprecated/games.js +313 -0
  82. package/tools/_deprecated/guessnumber.js +194 -0
  83. package/tools/_deprecated/hangman.js +129 -0
  84. package/tools/_deprecated/multiplayer-tictactoe.js +303 -0
  85. package/tools/_deprecated/recall.js +147 -0
  86. package/tools/_deprecated/remember.js +86 -0
  87. package/tools/_deprecated/riddle.js +240 -0
  88. package/tools/_deprecated/run-bootstrap.js +69 -0
  89. package/tools/_deprecated/skills-analytics.js +349 -0
  90. package/tools/_deprecated/skills-bootstrap.js +301 -0
  91. package/tools/_deprecated/skills-dashboard.js +268 -0
  92. package/tools/_deprecated/skills.js +380 -0
  93. package/tools/_deprecated/smart-intro.js +353 -0
  94. package/tools/_deprecated/storybuilder.js +331 -0
  95. package/tools/_deprecated/telegram-bot.js +183 -0
  96. package/tools/_deprecated/telegram-setup.js +214 -0
  97. package/tools/_deprecated/twentyquestions.js +143 -0
  98. package/tools/_discovery-enhanced.js +290 -0
  99. package/tools/_discovery.js +439 -0
  100. package/tools/_proactive-discovery.js +301 -0
  101. package/tools/_shared/index.js +64 -0
  102. package/tools/_shared.js +234 -0
  103. package/tools/_work-context.js +338 -0
  104. package/tools/_work-context.manual-test.js +199 -0
  105. package/tools/_work-context.test.js +260 -0
  106. package/tools/activity.js +220 -0
  107. package/tools/admin-inbox.js +218 -0
  108. package/tools/agent-treasury.js +288 -0
  109. package/tools/analytics.js +191 -0
  110. package/tools/approve.js +197 -0
  111. package/tools/arcade.js +173 -0
  112. package/tools/artifact-create.js +17 -11
  113. package/tools/artifact-view.js +31 -1
  114. package/tools/artifacts-price.js +47 -43
  115. package/tools/ask-expert.js +160 -0
  116. package/tools/available.js +120 -0
  117. package/tools/become-expert.js +150 -0
  118. package/tools/broadcast.js +286 -0
  119. package/tools/chat.js +202 -0
  120. package/tools/collaborative-drawing.js +286 -0
  121. package/tools/connection-status.js +178 -0
  122. package/tools/crossword.js +17 -1
  123. package/tools/discover.js +350 -34
  124. package/tools/dm.js +115 -73
  125. package/tools/drawing.js +1 -1
  126. package/tools/earnings.js +126 -0
  127. package/tools/echo.js +16 -0
  128. package/tools/feed.js +51 -7
  129. package/tools/follow.js +224 -0
  130. package/tools/friends.js +207 -0
  131. package/tools/game.js +2 -2
  132. package/tools/genesis.js +233 -0
  133. package/tools/gig-browse.js +206 -0
  134. package/tools/gig-complete.js +144 -0
  135. package/tools/handoff.js +7 -1
  136. package/tools/help.js +4 -4
  137. package/tools/idea.js +9 -2
  138. package/tools/inbox.js +334 -28
  139. package/tools/init.js +727 -36
  140. package/tools/invite.js +15 -4
  141. package/tools/l2-bridge.js +272 -0
  142. package/tools/l2-status.js +217 -0
  143. package/tools/l2.js +206 -0
  144. package/tools/migrate.js +156 -0
  145. package/tools/mint.js +377 -0
  146. package/tools/multiplayer-game.js +1 -1
  147. package/tools/notifications.js +415 -0
  148. package/tools/observe.js +200 -0
  149. package/tools/onboarding.js +147 -0
  150. package/tools/open.js +143 -12
  151. package/tools/party-game.js +2 -2
  152. package/tools/plan.js +225 -0
  153. package/tools/presence-agent.js +7 -0
  154. package/tools/proof-of-work.js +100 -104
  155. package/tools/pulse.js +218 -0
  156. package/tools/reply.js +166 -0
  157. package/tools/report.js +2 -2
  158. package/tools/reputation.js +175 -0
  159. package/tools/request.js +17 -3
  160. package/tools/schedule.js +367 -0
  161. package/tools/search-messages.js +123 -0
  162. package/tools/session.js +420 -0
  163. package/tools/session_price.js +128 -0
  164. package/tools/settings.js +126 -3
  165. package/tools/ship.js +31 -8
  166. package/tools/shipback.js +326 -0
  167. package/tools/smart-check.js +201 -0
  168. package/tools/social-processor.js +445 -0
  169. package/tools/solo-game.js +1 -1
  170. package/tools/start.js +335 -93
  171. package/tools/status.js +53 -6
  172. package/tools/stuck.js +297 -0
  173. package/tools/subscribe.js +148 -0
  174. package/tools/subscriptions.js +134 -0
  175. package/tools/suggest-tags.js +6 -8
  176. package/tools/tag-suggestions.js +257 -0
  177. package/tools/tip.js +193 -0
  178. package/tools/token.js +103 -0
  179. package/tools/update.js +1 -1
  180. package/tools/wallet.js +239 -186
  181. package/tools/watch.js +157 -0
  182. package/tools/webhook-test.js +388 -0
  183. package/tools/who.js +54 -3
  184. package/tools/withdraw.js +145 -0
  185. package/tools/work-summary.js +96 -0
  186. package/tools/workshop.js +327 -0
  187. package/tools/x-mentions.js +1 -1
  188. package/version.json +14 -3
  189. package/tools/artifacts-buy.js +0 -111
  190. package/tools/connect.js +0 -284
  191. package/tools/gigs-apply.js +0 -99
  192. package/tools/gigs-browse.js +0 -114
  193. package/tools/gigs-complete.js +0 -128
  194. package/tools/gigs-post.js +0 -140
  195. package/tools/live-off.js +0 -109
  196. package/tools/live-watch.js +0 -176
  197. package/tools/live.js +0 -128
  198. package/tools/sessions-browse.js +0 -105
  199. package/tools/whats-happening.js +0 -125
  200. /package/tools/{away.js → _deprecated/away.js} +0 -0
  201. /package/tools/{back.js → _deprecated/back.js} +0 -0
  202. /package/tools/{mute.js → _deprecated/mute.js} +0 -0
  203. /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
  204. /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
  205. /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
package/tools/wallet.js CHANGED
@@ -1,216 +1,269 @@
1
- const { ensureWallet, getWalletAddress, getBalance, hasWallet } = require('../../lib/cdp/wallet-helpers');
2
- const { sql } = require('../../api/lib/db');
3
-
4
1
  /**
5
- * vibe wallet - View wallet status, balance, or create wallet
2
+ * vibe_wallet - Unified wallet dashboard
3
+ *
4
+ * The "smoothbrain" view - everything in one place:
5
+ * - USD earnings (fiat)
6
+ * - $VIBE tokens (engagement currency)
7
+ * - Vesting schedule
8
+ * - Quick actions
6
9
  *
7
- * Commands:
8
- * vibe wallet # View balance/status
9
- * vibe wallet create # Explicitly create wallet
10
- * vibe wallet deposit # Show deposit instructions
11
- * vibe wallet withdraw # Withdraw to Coinbase/bank
12
- * vibe wallet history # Transaction history
10
+ * Examples:
11
+ * - "vibe wallet"
12
+ * - "check my balance"
13
+ * - "show my $VIBE"
13
14
  */
14
15
 
16
+ const fetch = require('node-fetch');
17
+
15
18
  module.exports = {
16
19
  name: 'vibe_wallet',
17
- description: 'Manage your /vibe wallet (USDC on Base)',
18
- parameters: {
20
+ description: 'Check your wallet: USD earnings, $VIBE tokens, vesting schedule, and available actions.',
21
+
22
+ inputSchema: {
19
23
  type: 'object',
20
24
  properties: {
21
- action: {
25
+ view: {
22
26
  type: 'string',
23
- enum: ['status', 'create', 'deposit', 'withdraw', 'history'],
24
- description: 'Action to perform (default: status)',
27
+ description: 'Which view: dashboard (default), vesting, history',
28
+ enum: ['dashboard', 'vesting', 'history'],
29
+ default: 'dashboard'
25
30
  },
26
- amount: {
31
+ limit: {
27
32
  type: 'number',
28
- description: 'Amount for withdraw (USDC)',
29
- },
30
- destination: {
31
- type: 'string',
32
- description: 'Destination address for withdrawal',
33
- },
34
- },
35
- },
36
-
37
- handler: async (args, context) => {
38
- const { action = 'status' } = args;
39
- const handle = context.handle; // From authenticated session
40
-
41
- if (!handle) {
42
- return {
43
- error: true,
44
- message: 'Not authenticated. Run: vibe init @yourhandle',
45
- };
33
+ description: 'Number of transactions/items to show (for history view)',
34
+ default: 10
35
+ }
46
36
  }
37
+ },
47
38
 
39
+ async execute({ view = 'dashboard', limit = 10 }, context) {
48
40
  try {
49
- // ACTION: Create wallet explicitly
50
- if (action === 'create') {
51
- const exists = await hasWallet(handle);
52
-
53
- if (exists) {
54
- const address = await getWalletAddress(handle);
55
- const balance = await getBalance(handle);
56
-
57
- return {
58
- success: false,
59
- message: `You already have a wallet!
60
-
61
- Address: ${address}
62
- Balance: $${balance.toFixed(2)} USDC
63
- Network: Base
64
-
65
- Commands:
66
- vibe wallet # View status
67
- vibe wallet deposit # Add funds
68
- `,
69
- };
70
- }
71
-
72
- // Create wallet
73
- const address = await ensureWallet(handle, 'explicit_create');
41
+ const handle = context.handle;
74
42
 
43
+ if (!handle) {
75
44
  return {
76
- success: true,
77
- address,
78
- message: `✓ Wallet created on Base!
79
-
80
- Address: ${address}
81
- Balance: $0.00 USDC
82
- Network: Base (Coinbase L2)
83
-
84
- Next steps:
85
- vibe wallet deposit # Add USDC to start transacting
86
- vibe wallet # Check balance anytime
87
-
88
- Your wallet is a smart contract - no gas fees, no seed phrases.
89
- Managed by Coinbase Developer Platform.
90
- `,
45
+ success: false,
46
+ error: 'Not authenticated. Use vibe init first.'
91
47
  };
92
48
  }
93
49
 
94
- // ACTION: Show deposit instructions
95
- if (action === 'deposit') {
96
- const address = await getWalletAddress(handle);
97
-
98
- if (!address) {
99
- return {
100
- error: true,
101
- message: `No wallet yet!
102
-
103
- You'll get a wallet automatically when:
104
- • Someone pays you
105
- • You pay for a service
50
+ const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
106
51
 
107
- Or create one now: vibe wallet create
108
- `,
109
- };
110
- }
111
-
112
- return {
113
- success: true,
114
- address,
115
- message: `💳 Deposit USDC to your /vibe wallet
116
-
117
- Address: ${address}
118
- Network: Base (Coinbase L2)
119
- Token: USDC
120
-
121
- Options:
122
- 1. Coinbase → Withdraw to this address (instant)
123
- 2. Bridge from Ethereum: https://bridge.base.org
124
- 3. Send USDC directly on Base network
125
-
126
- ⚠️ Important:
127
- - Only send USDC on Base network
128
- - Do NOT send ETH or other tokens (will be lost)
129
- - Minimum: $1 USDC
130
-
131
- Check balance: vibe wallet
132
- `,
133
- };
52
+ // Handle different views
53
+ if (view === 'vesting') {
54
+ return await fetchVestingSchedule(apiUrl, handle, context.token);
134
55
  }
135
56
 
136
- // ACTION: View status (default)
137
- const address = await getWalletAddress(handle);
138
-
139
- if (!address) {
140
- return {
141
- success: false,
142
- message: `No wallet yet!
143
-
144
- You'll get a wallet automatically when:
145
- • Someone pays you for helping
146
- • You pay for a service (like ping.money expert help)
147
- • You receive funds from another user
148
-
149
- This keeps /vibe simple - wallets emerge only when you transact.
150
-
151
- Want to create one now?
152
- vibe wallet create
153
-
154
- Questions?
155
- Wallets are smart contracts on Base (Coinbase's L2)
156
- No gas fees, no seed phrases, managed by CDP
157
- `,
158
- };
57
+ if (view === 'history') {
58
+ return await fetchHistory(apiUrl, handle, limit, context.token);
159
59
  }
160
60
 
161
- // Get balance
162
- const balance = await getBalance(handle);
163
-
164
- // Get recent transactions
165
- const cleanHandle = handle.replace('@', '');
166
- const txResult = await sql`
167
- SELECT event_type, amount, metadata, created_at
168
- FROM wallet_events
169
- WHERE handle = ${cleanHandle}
170
- ORDER BY created_at DESC
171
- LIMIT 5
172
- `;
173
-
174
- const recentTx = txResult.map(tx => {
175
- const amount = tx.amount ? `$${parseFloat(tx.amount).toFixed(2)}` : '';
176
- const type = tx.event_type;
177
- const date = new Date(tx.created_at).toLocaleDateString();
178
- return ` ${type.padEnd(12)} ${amount.padEnd(8)} ${date}`;
179
- });
61
+ // Default: dashboard view
62
+ return await fetchDashboard(apiUrl, handle, context.token);
180
63
 
181
- return {
182
- success: true,
183
- address,
184
- balance,
185
- message: `💰 Your /vibe Wallet
186
-
187
- Address: ${address.slice(0, 8)}...${address.slice(-6)}
188
- Balance: $${balance.toFixed(2)} USDC
189
- Network: Base (Coinbase L2)
190
-
191
- Recent activity:
192
- ${recentTx.length > 0 ? recentTx.join('\n') : ' No transactions yet'}
193
-
194
- Commands:
195
- vibe wallet deposit # Add funds
196
- vibe wallet withdraw # Send to Coinbase/bank
197
- vibe wallet history # Full transaction log
198
-
199
- View on Basescan:
200
- https://basescan.org/address/${address}
201
- `,
202
- };
203
64
  } catch (error) {
204
- console.error('Wallet error:', error);
205
-
206
65
  return {
207
- error: true,
208
- message: `Wallet error: ${error.message}
209
-
210
- If this persists, contact support or check:
211
- https://docs.cdp.coinbase.com/
212
- `,
66
+ success: false,
67
+ error: 'Failed to fetch wallet',
68
+ details: error.message
213
69
  };
214
70
  }
215
- },
71
+ }
216
72
  };
73
+
74
+ /**
75
+ * Fetch unified dashboard
76
+ */
77
+ async function fetchDashboard(apiUrl, handle, token) {
78
+ const response = await fetch(
79
+ `${apiUrl}/api/wallet/dashboard?handle=${handle}`,
80
+ { headers: { 'Authorization': `Bearer ${token}` } }
81
+ );
82
+
83
+ const result = await response.json();
84
+
85
+ if (!response.ok) {
86
+ return {
87
+ success: false,
88
+ error: 'Failed to fetch wallet dashboard',
89
+ details: result.error
90
+ };
91
+ }
92
+
93
+ // Build formatted output
94
+ const usd = result.usd;
95
+ const vibe = result.vibe;
96
+ const summary = result.summary;
97
+ const actions = result.actions || [];
98
+
99
+ // USD section
100
+ const usdSection = usd.available.cents > 0
101
+ ? `💵 USD Earnings
102
+ Available: ${usd.available.display}
103
+ ${usd.held.cents > 0 ? `Held: ${usd.held.display}\n ` : ''}Total earned: ${usd.total_earned.display}
104
+ ${usd.can_withdraw ? '✅ Ready to withdraw' : usd.payout_enabled ? '⏳ No balance to withdraw' : '⚠️ Connect bank to withdraw'}`
105
+ : `💵 USD Earnings
106
+ No earnings yet. Complete gigs to earn!`;
107
+
108
+ // $VIBE section
109
+ const vibeSection = `
110
+ 💜 $VIBE Tokens
111
+ Spendable: ${vibe.liquid} $VIBE
112
+ Vesting: ${vibe.vesting} $VIBE
113
+ ─────────────────
114
+ Total: ${vibe.total} $VIBE
115
+
116
+ ${vibe.upcoming.this_week > 0 ? `📅 +${vibe.upcoming.this_week} unlocks this week` : ''}
117
+ ${vibe.upcoming.this_month > 0 && vibe.upcoming.this_month !== vibe.upcoming.this_week ? `📅 +${vibe.upcoming.this_month} unlocks this month` : ''}
118
+ ${vibe.next_unlock ? `⏰ Next: ${vibe.next_unlock.amount} $VIBE in ${vibe.next_unlock.days} days` : ''}
119
+
120
+ Daily: ${vibe.daily.earned}/${vibe.daily.cap} earned today
121
+ ${'█'.repeat(Math.floor(vibe.daily.percent / 10))}${'░'.repeat(10 - Math.floor(vibe.daily.percent / 10))} ${vibe.daily.percent}%`;
122
+
123
+ // Actions section
124
+ const actionsSection = actions.length > 0
125
+ ? `\n🎯 Actions\n${actions.slice(0, 3).map(a => ` • ${a.label}: ${a.description}`).join('\n')}`
126
+ : '';
127
+
128
+ const formatted = `
129
+ ╭───────────────────────────────────╮
130
+ │ 💰 WALLET: @${handle.padEnd(17)}│
131
+ ╰───────────────────────────────────╯
132
+
133
+ ${usdSection}
134
+
135
+ ${vibeSection.trim()}
136
+ ${actionsSection}
137
+
138
+ Tip: Use "vibe wallet --view vesting" for full schedule
139
+ `.trim();
140
+
141
+ return {
142
+ success: true,
143
+ handle,
144
+ usd: result.usd,
145
+ vibe: result.vibe,
146
+ actions: result.actions,
147
+ formatted
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Fetch vesting schedule
153
+ */
154
+ async function fetchVestingSchedule(apiUrl, handle, token) {
155
+ const response = await fetch(
156
+ `${apiUrl}/api/vibe/vesting?handle=${handle}`,
157
+ { headers: { 'Authorization': `Bearer ${token}` } }
158
+ );
159
+
160
+ const result = await response.json();
161
+
162
+ if (!response.ok) {
163
+ return {
164
+ success: false,
165
+ error: 'Failed to fetch vesting schedule',
166
+ details: result.error
167
+ };
168
+ }
169
+
170
+ // Format vesting schedule
171
+ const summary = result.summary;
172
+ const unlocks = result.unlock_schedule || [];
173
+ const breakdown = result.activity_breakdown || [];
174
+
175
+ const scheduleLines = unlocks.slice(0, 8).map(week => {
176
+ const bar = '█'.repeat(Math.min(10, Math.ceil(week.total_amount / 10)));
177
+ return ` ${week.week_label.padEnd(12)} +${week.total_amount} $VIBE ${bar}`;
178
+ });
179
+
180
+ const breakdownLines = breakdown.slice(0, 5).map(act => {
181
+ return ` ${act.reason_display.padEnd(24)} ${act.amount} $VIBE`;
182
+ });
183
+
184
+ const formatted = `
185
+ ╭───────────────────────────────────╮
186
+ │ 📅 VESTING SCHEDULE: @${handle.padEnd(10)}│
187
+ ╰───────────────────────────────────╯
188
+
189
+ Summary
190
+ Liquid (spendable): ${summary.liquid} $VIBE
191
+ Vesting (locked): ${summary.vesting} $VIBE
192
+ ─────────────────────────────
193
+ Total: ${summary.total} $VIBE
194
+
195
+ ${result.next_unlock ? `Next Unlock
196
+ ${result.next_unlock.amount} $VIBE in ${result.next_unlock.days_remaining} days
197
+ (earned from: ${result.next_unlock.reason})
198
+ ` : ''}
199
+ Upcoming Unlocks by Week
200
+ ${scheduleLines.length > 0 ? scheduleLines.join('\n') : ' No tokens vesting'}
201
+
202
+ Earned By Activity
203
+ ${breakdownLines.length > 0 ? breakdownLines.join('\n') : ' No activity yet'}
204
+
205
+ ℹ️ Tokens vest over ${result.vesting_period_days} days to reward long-term builders.
206
+ `.trim();
207
+
208
+ return {
209
+ success: true,
210
+ handle,
211
+ summary: result.summary,
212
+ schedule: result.unlock_schedule,
213
+ breakdown: result.activity_breakdown,
214
+ formatted
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Fetch transaction history
220
+ */
221
+ async function fetchHistory(apiUrl, handle, limit, token) {
222
+ const response = await fetch(
223
+ `${apiUrl}/api/vibe/history?handle=${handle}&limit=${limit}`,
224
+ { headers: { 'Authorization': `Bearer ${token}` } }
225
+ );
226
+
227
+ const result = await response.json();
228
+
229
+ if (!response.ok) {
230
+ return {
231
+ success: false,
232
+ error: 'Failed to fetch history',
233
+ details: result.error
234
+ };
235
+ }
236
+
237
+ const transactions = result.transactions || [];
238
+
239
+ const txLines = transactions.map(tx => {
240
+ const icon = tx.amount > 0 ? '↓' : '↑';
241
+ const sign = tx.amount > 0 ? '+' : '';
242
+ const status = tx.status === 'vesting' ? '🔒' : '✓';
243
+ const date = new Date(tx.created_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
244
+ return ` ${icon} ${sign}${tx.amount} $VIBE ${tx.reason.padEnd(20)} ${status} ${date}`;
245
+ });
246
+
247
+ const formatted = `
248
+ ╭───────────────────────────────────╮
249
+ │ 📜 HISTORY: @${handle.padEnd(17)}│
250
+ ╰───────────────────────────────────╯
251
+
252
+ Balance: ${result.balance.liquid} liquid / ${result.balance.vesting} vesting
253
+
254
+ Recent Transactions
255
+ ${txLines.length > 0 ? txLines.join('\n') : ' No transactions yet'}
256
+
257
+ ${result.pagination.has_more ? `\n(showing ${limit} of more...)` : ''}
258
+
259
+ Legend: ↓ earned ↑ spent 🔒 vesting ✓ liquid
260
+ `.trim();
261
+
262
+ return {
263
+ success: true,
264
+ handle,
265
+ balance: result.balance,
266
+ transactions: result.transactions,
267
+ formatted
268
+ };
269
+ }
package/tools/watch.js ADDED
@@ -0,0 +1,157 @@
1
+ /**
2
+ * vibe watch — Watch live broadcasts
3
+ *
4
+ * Discover and watch live coding sessions from the /vibe community.
5
+ *
6
+ * Commands:
7
+ * - watch → List active broadcasts
8
+ * - watch @handle → Watch specific person's broadcast
9
+ * - watch room_xxx → Watch by room ID
10
+ */
11
+
12
+ const config = require('../config');
13
+
14
+ const definition = {
15
+ name: 'vibe_watch',
16
+ description: 'See who\'s live and watch their coding sessions in real-time.',
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ target: {
21
+ type: 'string',
22
+ description: 'Handle (@user) or room ID to watch. Omit to list all live broadcasts.'
23
+ }
24
+ }
25
+ }
26
+ };
27
+
28
+ async function handler(args) {
29
+ const apiUrl = config.getApiUrl();
30
+ const target = args.target;
31
+
32
+ // LIST all broadcasts
33
+ if (!target) {
34
+ try {
35
+ const response = await fetch(`${apiUrl}/api/live`, {
36
+ headers: { 'Accept': 'application/json' }
37
+ });
38
+
39
+ const data = await response.json();
40
+
41
+ if (!data.success) {
42
+ return { display: `⚠️ Failed to fetch live broadcasts: ${data.error}` };
43
+ }
44
+
45
+ if (!data.broadcasts || data.broadcasts.length === 0) {
46
+ let display = `📺 **No Live Broadcasts**\n\n`;
47
+ display += `_No one is streaming right now._\n\n`;
48
+ display += `**Start your own:**\n`;
49
+ display += `\`vibe broadcast start "What you're building"\`\n\n`;
50
+ display += `**Browse saved sessions:**\n`;
51
+ display += `\`vibe session list\``;
52
+ return { display };
53
+ }
54
+
55
+ const formatDuration = (secs) => {
56
+ if (secs < 60) return 'just started';
57
+ if (secs < 3600) return `${Math.floor(secs / 60)}m`;
58
+ return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
59
+ };
60
+
61
+ let display = `📺 **${data.broadcasts.length} Live Now**\n\n`;
62
+
63
+ for (const b of data.broadcasts) {
64
+ const viewers = b.viewers || 0;
65
+ const duration = formatDuration(b.duration || 0);
66
+
67
+ display += `🔴 **@${b.handle}**`;
68
+ if (viewers > 0) {
69
+ display += ` — ${viewers} watching`;
70
+ }
71
+ display += ` — ${duration}\n`;
72
+ display += ` → \`vibe watch ${b.roomId}\`\n\n`;
73
+ }
74
+
75
+ display += `_Watch a stream or start your own with \`vibe broadcast start\`_`;
76
+
77
+ return { display };
78
+
79
+ } catch (error) {
80
+ return { display: `## Watch Error\n\n${error.message}` };
81
+ }
82
+ }
83
+
84
+ // WATCH specific broadcast
85
+ const isRoomId = target.startsWith('room_');
86
+ const isHandle = target.startsWith('@') || !target.includes('_');
87
+
88
+ try {
89
+ let roomId = target;
90
+ let broadcasterHandle = null;
91
+
92
+ // If target is a handle, find their room
93
+ if (isHandle) {
94
+ broadcasterHandle = target.replace('@', '').toLowerCase();
95
+
96
+ // Fetch live broadcasts to find this person's room
97
+ const liveResponse = await fetch(`${apiUrl}/api/live`, {
98
+ headers: { 'Accept': 'application/json' }
99
+ });
100
+
101
+ const liveData = await liveResponse.json();
102
+
103
+ if (!liveData.success || !liveData.broadcasts) {
104
+ return { display: `⚠️ Failed to fetch broadcasts` };
105
+ }
106
+
107
+ const broadcast = liveData.broadcasts.find(
108
+ b => b.handle.toLowerCase() === broadcasterHandle
109
+ );
110
+
111
+ if (!broadcast) {
112
+ return {
113
+ display: `📺 @${broadcasterHandle} is not live right now.\n\n_Check who's streaming with \`vibe watch\`_`
114
+ };
115
+ }
116
+
117
+ roomId = broadcast.roomId;
118
+ }
119
+
120
+ // Get broadcast info
121
+ const response = await fetch(`${apiUrl}/api/watch?room=${roomId}`);
122
+ const data = await response.json();
123
+
124
+ if (!data.success) {
125
+ return {
126
+ display: `📺 Broadcast not found or ended.\n\n_Check live streams with \`vibe watch\`_`
127
+ };
128
+ }
129
+
130
+ const broadcast = data.broadcast;
131
+ const duration = broadcast.startedAt
132
+ ? Math.round((Date.now() - new Date(broadcast.startedAt).getTime()) / 1000)
133
+ : 0;
134
+
135
+ const formatDuration = (secs) => {
136
+ if (secs < 60) return 'just started';
137
+ if (secs < 3600) return `${Math.floor(secs / 60)}m`;
138
+ return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
139
+ };
140
+
141
+ let display = `🔴 **LIVE: @${broadcast.handle}**\n\n`;
142
+ display += `**Duration:** ${formatDuration(duration)}\n`;
143
+ display += `**Viewers:** ${broadcast.viewerCount || 0}\n\n`;
144
+ display += `**Watch in browser:**\n`;
145
+ display += `https://slashvibe.dev/watch/${roomId}\n\n`;
146
+ display += `_Open the URL above to watch the live terminal stream._\n\n`;
147
+ display += `---\n`;
148
+ display += `_Coming soon: inline terminal replay in Claude Code_`;
149
+
150
+ return { display };
151
+
152
+ } catch (error) {
153
+ return { display: `## Watch Error\n\n${error.message}` };
154
+ }
155
+ }
156
+
157
+ module.exports = { definition, handler };