slashvibe-mcp 0.3.21 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/auto-update.js +10 -15
  4. package/config.js +36 -31
  5. package/crypto.js +1 -6
  6. package/debug.js +12 -0
  7. package/discord.js +19 -19
  8. package/eslint.config.js +54 -0
  9. package/index.js +217 -207
  10. package/intelligence/index.js +2 -9
  11. package/intelligence/infer.js +10 -16
  12. package/intelligence/patterns.js +23 -18
  13. package/intelligence/proactive.js +16 -15
  14. package/intelligence/serendipity.js +57 -20
  15. package/memory.js +13 -8
  16. package/migrate-v2.js +72 -0
  17. package/notification-emitter.js +2 -2
  18. package/notify.js +39 -14
  19. package/package.json +28 -29
  20. package/post-install.js +141 -0
  21. package/presence.js +2 -2
  22. package/prompts.js +5 -9
  23. package/protocol/index.js +123 -87
  24. package/protocol/telegram-commands.js +36 -37
  25. package/store/api.js +358 -529
  26. package/store/local.js +9 -10
  27. package/store/profiles.js +48 -192
  28. package/store/reservations.js +2 -9
  29. package/store/skills.js +69 -71
  30. package/store/sqlite.js +355 -0
  31. package/test-skills-bootstrap.js +20 -0
  32. package/test-v2-integration.js +385 -0
  33. package/tools/_actions.js +48 -387
  34. package/tools/_connection-queue.js +45 -56
  35. package/tools/_discovery-enhanced.js +52 -57
  36. package/tools/_discovery.js +87 -185
  37. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  38. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  39. package/tools/_proactive-discovery.js +60 -73
  40. package/tools/_shared/index.js +41 -64
  41. package/tools/admin-inbox.js +10 -15
  42. package/tools/agents.js +1 -1
  43. package/tools/artifact-create.js +13 -23
  44. package/tools/artifact-view.js +4 -4
  45. package/tools/{_deprecated/back.js → back.js} +1 -1
  46. package/tools/bye.js +3 -5
  47. package/tools/consent.js +2 -2
  48. package/tools/context.js +9 -10
  49. package/tools/crossword.js +3 -2
  50. package/tools/discover.js +94 -356
  51. package/tools/dm.js +27 -86
  52. package/tools/doctor.js +12 -41
  53. package/tools/drawing.js +34 -20
  54. package/tools/echo.js +11 -11
  55. package/tools/feed.js +30 -58
  56. package/tools/follow.js +64 -187
  57. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  58. package/tools/game.js +144 -48
  59. package/tools/handoff.js +6 -8
  60. package/tools/help.js +3 -3
  61. package/tools/idea.js +15 -27
  62. package/tools/inbox.js +121 -293
  63. package/tools/init.js +54 -151
  64. package/tools/invite.js +8 -21
  65. package/tools/migrate.js +27 -24
  66. package/tools/multiplayer-game.js +50 -40
  67. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  68. package/tools/notifications.js +58 -48
  69. package/tools/observe.js +12 -15
  70. package/tools/onboarding.js +8 -11
  71. package/tools/open.js +13 -144
  72. package/tools/party-game.js +23 -12
  73. package/tools/patterns.js +2 -1
  74. package/tools/ping.js +5 -7
  75. package/tools/react.js +28 -30
  76. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  77. package/tools/release.js +4 -2
  78. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  79. package/tools/report.js +2 -2
  80. package/tools/request.js +6 -26
  81. package/tools/reserve.js +1 -1
  82. package/tools/session-fork.js +97 -0
  83. package/tools/session-save.js +109 -0
  84. package/tools/settings.js +30 -99
  85. package/tools/ship.js +74 -56
  86. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  87. package/tools/social-inbox.js +22 -28
  88. package/tools/social-post.js +24 -27
  89. package/tools/solo-game.js +54 -46
  90. package/tools/start.js +14 -148
  91. package/tools/status.js +21 -68
  92. package/tools/submit.js +4 -2
  93. package/tools/suggest-tags.js +36 -33
  94. package/tools/summarize.js +19 -16
  95. package/tools/tag-suggestions.js +72 -73
  96. package/tools/test.js +1 -1
  97. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  98. package/tools/token.js +4 -4
  99. package/tools/update.js +1 -2
  100. package/tools/watch.js +132 -112
  101. package/tools/who.js +20 -40
  102. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  103. package/tools/workshop-buddy.js +52 -53
  104. package/tools/x-mentions.js +0 -1
  105. package/tools/x-reply.js +0 -1
  106. package/twitter.js +14 -20
  107. package/version.json +8 -10
  108. package/webhook-runner.js +132 -0
  109. package/auth-store.js +0 -148
  110. package/bridges/bridge-monitor.js +0 -388
  111. package/bridges/discord-bot.js +0 -431
  112. package/bridges/farcaster.js +0 -299
  113. package/bridges/telegram.js +0 -261
  114. package/bridges/webhook-health.js +0 -420
  115. package/bridges/webhook-server.js +0 -437
  116. package/bridges/whatsapp.js +0 -441
  117. package/bridges/x-webhook.js +0 -423
  118. package/games/arcade.js +0 -406
  119. package/games/chess.js +0 -451
  120. package/games/colorguess.js +0 -343
  121. package/games/crossword-words.js +0 -171
  122. package/games/crossword.js +0 -461
  123. package/games/drawing.js +0 -347
  124. package/games/gameroulette.js +0 -300
  125. package/games/gamerouter.js +0 -336
  126. package/games/gamestatus.js +0 -337
  127. package/games/guessnumber.js +0 -209
  128. package/games/hangman.js +0 -279
  129. package/games/memory.js +0 -338
  130. package/games/multiplayer-tictactoe.js +0 -389
  131. package/games/pixelart.js +0 -399
  132. package/games/quickduel.js +0 -354
  133. package/games/riddle.js +0 -371
  134. package/games/rockpaperscissors.js +0 -291
  135. package/games/snake.js +0 -406
  136. package/games/storybuilder.js +0 -343
  137. package/games/tictactoe.js +0 -345
  138. package/games/twentyquestions.js +0 -286
  139. package/games/twotruths.js +0 -207
  140. package/games/werewolf.js +0 -508
  141. package/games/wordassociation.js +0 -247
  142. package/games/wordchain.js +0 -135
  143. package/intelligence/interests.js +0 -369
  144. package/setup.js +0 -480
  145. package/smart-inbox.js +0 -276
  146. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  147. package/tools/_deprecated/bootstrap-skills.js +0 -231
  148. package/tools/_deprecated/bridge-dashboard.js +0 -342
  149. package/tools/_deprecated/bridge-health.js +0 -400
  150. package/tools/_deprecated/bridge-live.js +0 -384
  151. package/tools/_deprecated/bridges.js +0 -383
  152. package/tools/_deprecated/colorguess.js +0 -281
  153. package/tools/_deprecated/discover-insights.js +0 -379
  154. package/tools/_deprecated/discover-momentum.js +0 -256
  155. package/tools/_deprecated/discovery-analytics.js +0 -345
  156. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  157. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  158. package/tools/_deprecated/discovery-daily.js +0 -375
  159. package/tools/_deprecated/discovery-dashboard.js +0 -385
  160. package/tools/_deprecated/discovery-digest.js +0 -314
  161. package/tools/_deprecated/discovery-hub.js +0 -357
  162. package/tools/_deprecated/discovery-insights.js +0 -384
  163. package/tools/_deprecated/discovery-momentum.js +0 -281
  164. package/tools/_deprecated/discovery-monitor.js +0 -319
  165. package/tools/_deprecated/discovery-proactive.js +0 -300
  166. package/tools/_deprecated/draw.js +0 -317
  167. package/tools/_deprecated/farcaster.js +0 -307
  168. package/tools/_deprecated/games-catalog.js +0 -376
  169. package/tools/_deprecated/games.js +0 -313
  170. package/tools/_deprecated/guessnumber.js +0 -194
  171. package/tools/_deprecated/hangman.js +0 -129
  172. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  173. package/tools/_deprecated/riddle.js +0 -240
  174. package/tools/_deprecated/run-bootstrap.js +0 -69
  175. package/tools/_deprecated/skills-analytics.js +0 -349
  176. package/tools/_deprecated/skills-bootstrap.js +0 -301
  177. package/tools/_deprecated/skills-dashboard.js +0 -268
  178. package/tools/_deprecated/skills.js +0 -380
  179. package/tools/_deprecated/smart-intro.js +0 -353
  180. package/tools/_deprecated/storybuilder.js +0 -331
  181. package/tools/_deprecated/telegram-bot.js +0 -183
  182. package/tools/_deprecated/telegram-setup.js +0 -214
  183. package/tools/_deprecated/twentyquestions.js +0 -143
  184. package/tools/_shared.js +0 -234
  185. package/tools/_work-context.js +0 -338
  186. package/tools/_work-context.manual-test.js +0 -199
  187. package/tools/_work-context.test.js +0 -260
  188. package/tools/activity.js +0 -220
  189. package/tools/agent-treasury.js +0 -288
  190. package/tools/analytics.js +0 -191
  191. package/tools/approve.js +0 -197
  192. package/tools/arcade.js +0 -173
  193. package/tools/artifacts-price.js +0 -107
  194. package/tools/ask-expert.js +0 -160
  195. package/tools/available.js +0 -120
  196. package/tools/become-expert.js +0 -150
  197. package/tools/broadcast.js +0 -325
  198. package/tools/chat.js +0 -202
  199. package/tools/collaborative-drawing.js +0 -286
  200. package/tools/connection-status.js +0 -178
  201. package/tools/earnings.js +0 -126
  202. package/tools/friends.js +0 -207
  203. package/tools/genesis.js +0 -233
  204. package/tools/gig-browse.js +0 -206
  205. package/tools/gig-complete.js +0 -144
  206. package/tools/health.js +0 -87
  207. package/tools/leaderboard.js +0 -117
  208. package/tools/lib/git-apply.js +0 -206
  209. package/tools/lib/git-bundle.js +0 -407
  210. package/tools/mint.js +0 -377
  211. package/tools/plan.js +0 -225
  212. package/tools/profile.js +0 -219
  213. package/tools/proof-of-work.js +0 -144
  214. package/tools/pulse.js +0 -218
  215. package/tools/reply.js +0 -166
  216. package/tools/reputation.js +0 -175
  217. package/tools/schedule.js +0 -367
  218. package/tools/search-messages.js +0 -123
  219. package/tools/session.js +0 -467
  220. package/tools/session_price.js +0 -128
  221. package/tools/smart-check.js +0 -201
  222. package/tools/social-processor.js +0 -445
  223. package/tools/streak.js +0 -147
  224. package/tools/stuck.js +0 -297
  225. package/tools/subscribe.js +0 -148
  226. package/tools/subscriptions.js +0 -134
  227. package/tools/tip.js +0 -193
  228. package/tools/wallet.js +0 -269
  229. package/tools/webhook-test.js +0 -388
  230. package/tools/withdraw.js +0 -145
  231. package/tools/work-summary.js +0 -96
  232. package/tools/workshop.js +0 -327
  233. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  234. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  235. /package/tools/{_deprecated/away.js → away.js} +0 -0
package/tools/tip.js DELETED
@@ -1,193 +0,0 @@
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/wallet.js DELETED
@@ -1,269 +0,0 @@
1
- /**
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
9
- *
10
- * Examples:
11
- * - "vibe wallet"
12
- * - "check my balance"
13
- * - "show my $VIBE"
14
- */
15
-
16
- const fetch = require('node-fetch');
17
-
18
- module.exports = {
19
- name: 'vibe_wallet',
20
- description: 'Check your wallet: USD earnings, $VIBE tokens, vesting schedule, and available actions.',
21
-
22
- inputSchema: {
23
- type: 'object',
24
- properties: {
25
- view: {
26
- type: 'string',
27
- description: 'Which view: dashboard (default), vesting, history',
28
- enum: ['dashboard', 'vesting', 'history'],
29
- default: 'dashboard'
30
- },
31
- limit: {
32
- type: 'number',
33
- description: 'Number of transactions/items to show (for history view)',
34
- default: 10
35
- }
36
- }
37
- },
38
-
39
- async execute({ view = 'dashboard', limit = 10 }, context) {
40
- try {
41
- const handle = context.handle;
42
-
43
- if (!handle) {
44
- return {
45
- success: false,
46
- error: 'Not authenticated. Use vibe init first.'
47
- };
48
- }
49
-
50
- const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
51
-
52
- // Handle different views
53
- if (view === 'vesting') {
54
- return await fetchVestingSchedule(apiUrl, handle, context.token);
55
- }
56
-
57
- if (view === 'history') {
58
- return await fetchHistory(apiUrl, handle, limit, context.token);
59
- }
60
-
61
- // Default: dashboard view
62
- return await fetchDashboard(apiUrl, handle, context.token);
63
-
64
- } catch (error) {
65
- return {
66
- success: false,
67
- error: 'Failed to fetch wallet',
68
- details: error.message
69
- };
70
- }
71
- }
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
- }