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.
- package/LICENSE +21 -0
- package/README.md +280 -47
- package/auto-update.js +10 -15
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/debug.js +12 -0
- package/discord.js +19 -19
- package/eslint.config.js +54 -0
- package/index.js +217 -207
- package/intelligence/index.js +2 -9
- package/intelligence/infer.js +10 -16
- package/intelligence/patterns.js +23 -18
- package/intelligence/proactive.js +16 -15
- package/intelligence/serendipity.js +57 -20
- package/memory.js +13 -8
- package/migrate-v2.js +72 -0
- package/notification-emitter.js +2 -2
- package/notify.js +39 -14
- package/package.json +28 -29
- package/post-install.js +141 -0
- package/presence.js +2 -2
- package/prompts.js +5 -9
- package/protocol/index.js +123 -87
- package/protocol/telegram-commands.js +36 -37
- package/store/api.js +358 -529
- package/store/local.js +9 -10
- package/store/profiles.js +48 -192
- package/store/reservations.js +2 -9
- package/store/skills.js +69 -71
- package/store/sqlite.js +355 -0
- package/test-skills-bootstrap.js +20 -0
- package/test-v2-integration.js +385 -0
- package/tools/_actions.js +48 -387
- package/tools/_connection-queue.js +45 -56
- package/tools/_discovery-enhanced.js +52 -57
- package/tools/_discovery.js +87 -185
- package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
- package/tools/{shipback.js → _experimental/shipback.js} +4 -3
- package/tools/_proactive-discovery.js +60 -73
- package/tools/_shared/index.js +41 -64
- package/tools/admin-inbox.js +10 -15
- package/tools/agents.js +1 -1
- package/tools/artifact-create.js +13 -23
- package/tools/artifact-view.js +4 -4
- package/tools/{_deprecated/back.js → back.js} +1 -1
- package/tools/bye.js +3 -5
- package/tools/consent.js +2 -2
- package/tools/context.js +9 -10
- package/tools/crossword.js +3 -2
- package/tools/discover.js +94 -356
- package/tools/dm.js +27 -86
- package/tools/doctor.js +12 -41
- package/tools/drawing.js +34 -20
- package/tools/echo.js +11 -11
- package/tools/feed.js +30 -58
- package/tools/follow.js +64 -187
- package/tools/{_deprecated/forget.js → forget.js} +4 -7
- package/tools/game.js +144 -48
- package/tools/handoff.js +6 -8
- package/tools/help.js +3 -3
- package/tools/idea.js +15 -27
- package/tools/inbox.js +121 -293
- package/tools/init.js +54 -151
- package/tools/invite.js +8 -21
- package/tools/migrate.js +27 -24
- package/tools/multiplayer-game.js +50 -40
- package/tools/{_deprecated/mute.js → mute.js} +4 -3
- package/tools/notifications.js +58 -48
- package/tools/observe.js +12 -15
- package/tools/onboarding.js +8 -11
- package/tools/open.js +13 -144
- package/tools/party-game.js +23 -12
- package/tools/patterns.js +2 -1
- package/tools/ping.js +5 -7
- package/tools/react.js +28 -30
- package/tools/{_deprecated/recall.js → recall.js} +5 -10
- package/tools/release.js +4 -2
- package/tools/{_deprecated/remember.js → remember.js} +4 -6
- package/tools/report.js +2 -2
- package/tools/request.js +6 -26
- package/tools/reserve.js +1 -1
- package/tools/session-fork.js +97 -0
- package/tools/session-save.js +109 -0
- package/tools/settings.js +30 -99
- package/tools/ship.js +74 -56
- package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
- package/tools/social-inbox.js +22 -28
- package/tools/social-post.js +24 -27
- package/tools/solo-game.js +54 -46
- package/tools/start.js +14 -148
- package/tools/status.js +21 -68
- package/tools/submit.js +4 -2
- package/tools/suggest-tags.js +36 -33
- package/tools/summarize.js +19 -16
- package/tools/tag-suggestions.js +72 -73
- package/tools/test.js +1 -1
- package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
- package/tools/token.js +4 -4
- package/tools/update.js +1 -2
- package/tools/watch.js +132 -112
- package/tools/who.js +20 -40
- package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
- package/tools/workshop-buddy.js +52 -53
- package/tools/x-mentions.js +0 -1
- package/tools/x-reply.js +0 -1
- package/twitter.js +14 -20
- package/version.json +8 -10
- package/webhook-runner.js +132 -0
- package/auth-store.js +0 -148
- package/bridges/bridge-monitor.js +0 -388
- package/bridges/discord-bot.js +0 -431
- package/bridges/farcaster.js +0 -299
- package/bridges/telegram.js +0 -261
- package/bridges/webhook-health.js +0 -420
- package/bridges/webhook-server.js +0 -437
- package/bridges/whatsapp.js +0 -441
- package/bridges/x-webhook.js +0 -423
- package/games/arcade.js +0 -406
- package/games/chess.js +0 -451
- package/games/colorguess.js +0 -343
- package/games/crossword-words.js +0 -171
- package/games/crossword.js +0 -461
- package/games/drawing.js +0 -347
- package/games/gameroulette.js +0 -300
- package/games/gamerouter.js +0 -336
- package/games/gamestatus.js +0 -337
- package/games/guessnumber.js +0 -209
- package/games/hangman.js +0 -279
- package/games/memory.js +0 -338
- package/games/multiplayer-tictactoe.js +0 -389
- package/games/pixelart.js +0 -399
- package/games/quickduel.js +0 -354
- package/games/riddle.js +0 -371
- package/games/rockpaperscissors.js +0 -291
- package/games/snake.js +0 -406
- package/games/storybuilder.js +0 -343
- package/games/tictactoe.js +0 -345
- package/games/twentyquestions.js +0 -286
- package/games/twotruths.js +0 -207
- package/games/werewolf.js +0 -508
- package/games/wordassociation.js +0 -247
- package/games/wordchain.js +0 -135
- package/intelligence/interests.js +0 -369
- package/setup.js +0 -480
- package/smart-inbox.js +0 -276
- package/tools/_deprecated/auto-suggest-connections.js +0 -304
- package/tools/_deprecated/bootstrap-skills.js +0 -231
- package/tools/_deprecated/bridge-dashboard.js +0 -342
- package/tools/_deprecated/bridge-health.js +0 -400
- package/tools/_deprecated/bridge-live.js +0 -384
- package/tools/_deprecated/bridges.js +0 -383
- package/tools/_deprecated/colorguess.js +0 -281
- package/tools/_deprecated/discover-insights.js +0 -379
- package/tools/_deprecated/discover-momentum.js +0 -256
- package/tools/_deprecated/discovery-analytics.js +0 -345
- package/tools/_deprecated/discovery-auto-suggest.js +0 -275
- package/tools/_deprecated/discovery-bootstrap.js +0 -267
- package/tools/_deprecated/discovery-daily.js +0 -375
- package/tools/_deprecated/discovery-dashboard.js +0 -385
- package/tools/_deprecated/discovery-digest.js +0 -314
- package/tools/_deprecated/discovery-hub.js +0 -357
- package/tools/_deprecated/discovery-insights.js +0 -384
- package/tools/_deprecated/discovery-momentum.js +0 -281
- package/tools/_deprecated/discovery-monitor.js +0 -319
- package/tools/_deprecated/discovery-proactive.js +0 -300
- package/tools/_deprecated/draw.js +0 -317
- package/tools/_deprecated/farcaster.js +0 -307
- package/tools/_deprecated/games-catalog.js +0 -376
- package/tools/_deprecated/games.js +0 -313
- package/tools/_deprecated/guessnumber.js +0 -194
- package/tools/_deprecated/hangman.js +0 -129
- package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
- package/tools/_deprecated/riddle.js +0 -240
- package/tools/_deprecated/run-bootstrap.js +0 -69
- package/tools/_deprecated/skills-analytics.js +0 -349
- package/tools/_deprecated/skills-bootstrap.js +0 -301
- package/tools/_deprecated/skills-dashboard.js +0 -268
- package/tools/_deprecated/skills.js +0 -380
- package/tools/_deprecated/smart-intro.js +0 -353
- package/tools/_deprecated/storybuilder.js +0 -331
- package/tools/_deprecated/telegram-bot.js +0 -183
- package/tools/_deprecated/telegram-setup.js +0 -214
- package/tools/_deprecated/twentyquestions.js +0 -143
- package/tools/_shared.js +0 -234
- package/tools/_work-context.js +0 -338
- package/tools/_work-context.manual-test.js +0 -199
- package/tools/_work-context.test.js +0 -260
- package/tools/activity.js +0 -220
- package/tools/agent-treasury.js +0 -288
- package/tools/analytics.js +0 -191
- package/tools/approve.js +0 -197
- package/tools/arcade.js +0 -173
- package/tools/artifacts-price.js +0 -107
- package/tools/ask-expert.js +0 -160
- package/tools/available.js +0 -120
- package/tools/become-expert.js +0 -150
- package/tools/broadcast.js +0 -325
- package/tools/chat.js +0 -202
- package/tools/collaborative-drawing.js +0 -286
- package/tools/connection-status.js +0 -178
- package/tools/earnings.js +0 -126
- package/tools/friends.js +0 -207
- package/tools/genesis.js +0 -233
- package/tools/gig-browse.js +0 -206
- package/tools/gig-complete.js +0 -144
- package/tools/health.js +0 -87
- package/tools/leaderboard.js +0 -117
- package/tools/lib/git-apply.js +0 -206
- package/tools/lib/git-bundle.js +0 -407
- package/tools/mint.js +0 -377
- package/tools/plan.js +0 -225
- package/tools/profile.js +0 -219
- package/tools/proof-of-work.js +0 -144
- package/tools/pulse.js +0 -218
- package/tools/reply.js +0 -166
- package/tools/reputation.js +0 -175
- package/tools/schedule.js +0 -367
- package/tools/search-messages.js +0 -123
- package/tools/session.js +0 -467
- package/tools/session_price.js +0 -128
- package/tools/smart-check.js +0 -201
- package/tools/social-processor.js +0 -445
- package/tools/streak.js +0 -147
- package/tools/stuck.js +0 -297
- package/tools/subscribe.js +0 -148
- package/tools/subscriptions.js +0 -134
- package/tools/tip.js +0 -193
- package/tools/wallet.js +0 -269
- package/tools/webhook-test.js +0 -388
- package/tools/withdraw.js +0 -145
- package/tools/work-summary.js +0 -96
- package/tools/workshop.js +0 -327
- /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
- /package/tools/{l2.js → _experimental/l2.js} +0 -0
- /package/tools/{_deprecated/away.js → away.js} +0 -0
package/tools/feed.js
CHANGED
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const config = require('../config');
|
|
16
|
-
const { requireInit, header, emptyState, formatTimeAgo, divider
|
|
17
|
-
const { actions, formatActions } = require('./_actions');
|
|
16
|
+
const { requireInit, normalizeHandle, header, emptyState, formatTimeAgo, divider } = require('./_shared');
|
|
18
17
|
|
|
19
18
|
const definition = {
|
|
20
19
|
name: 'vibe_feed',
|
|
@@ -44,21 +43,21 @@ const definition = {
|
|
|
44
43
|
};
|
|
45
44
|
|
|
46
45
|
const TYPE_CONFIG = {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
idea: { emoji: '💡', label: 'idea', verb: 'had an idea' },
|
|
47
|
+
riff: { emoji: '↳', label: 'riff', verb: 'riffed' },
|
|
48
|
+
shipped: { emoji: '🚀', label: 'ship', verb: 'shipped' },
|
|
49
|
+
request: { emoji: '🔓', label: 'request', verb: 'requested' },
|
|
50
|
+
claim: { emoji: '🔨', label: 'claim', verb: 'claimed' },
|
|
51
|
+
observation: { emoji: '👁️', label: 'observation', verb: 'observed' },
|
|
52
|
+
general: { emoji: '📝', label: 'post', verb: 'posted' }
|
|
54
53
|
};
|
|
55
54
|
|
|
56
55
|
const CATEGORY_MAP = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
ideas: ['idea', 'riff'],
|
|
57
|
+
ships: ['shipped'],
|
|
58
|
+
requests: ['request', 'claim'],
|
|
59
|
+
riffs: ['riff'],
|
|
60
|
+
observations: ['observation']
|
|
62
61
|
};
|
|
63
62
|
|
|
64
63
|
async function handler(args) {
|
|
@@ -89,39 +88,18 @@ async function handler(args) {
|
|
|
89
88
|
|
|
90
89
|
// Filter by author if specified
|
|
91
90
|
if (args.from) {
|
|
92
|
-
const targetHandle = args.from
|
|
91
|
+
const targetHandle = normalizeHandle(args.from);
|
|
93
92
|
entries = entries.filter(e => e.author.toLowerCase() === targetHandle);
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
// Filter by tag if specified
|
|
97
96
|
if (args.tag) {
|
|
98
97
|
const tag = args.tag.toLowerCase();
|
|
99
|
-
entries = entries.filter(e =>
|
|
100
|
-
e.tags && e.tags.some(t => t.toLowerCase().includes(tag))
|
|
101
|
-
);
|
|
98
|
+
entries = entries.filter(e => e.tags && e.tags.some(t => t.toLowerCase().includes(tag)));
|
|
102
99
|
}
|
|
103
100
|
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
const relevantHandles = new Set();
|
|
107
|
-
try {
|
|
108
|
-
const relevancy = await fetchRelevantUsers(myHandle, 'feed', 20);
|
|
109
|
-
if (relevancy && relevancy.matches) {
|
|
110
|
-
relevancy.matches.forEach(m => relevantHandles.add(m.handle.toLowerCase()));
|
|
111
|
-
}
|
|
112
|
-
} catch (e) {
|
|
113
|
-
// Don't fail feed if relevancy fails
|
|
114
|
-
console.log('[feed] relevancy fetch error:', e.message);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Sort by timestamp with relevancy boost
|
|
118
|
-
// Relevant users get a 6-hour boost (their content appears "more recent")
|
|
119
|
-
const RELEVANCY_BOOST_MS = 6 * 60 * 60 * 1000; // 6 hours in ms
|
|
120
|
-
entries.sort((a, b) => {
|
|
121
|
-
const aBoost = relevantHandles.has(a.author.toLowerCase()) ? RELEVANCY_BOOST_MS : 0;
|
|
122
|
-
const bBoost = relevantHandles.has(b.author.toLowerCase()) ? RELEVANCY_BOOST_MS : 0;
|
|
123
|
-
return (b.timestamp + bBoost) - (a.timestamp + aBoost);
|
|
124
|
-
});
|
|
101
|
+
// Sort by timestamp (newest first)
|
|
102
|
+
entries.sort((a, b) => b.timestamp - a.timestamp);
|
|
125
103
|
|
|
126
104
|
// Limit
|
|
127
105
|
entries = entries.slice(0, limit);
|
|
@@ -131,7 +109,7 @@ async function handler(args) {
|
|
|
131
109
|
let suggestion = 'Start something! "idea: [your idea]"';
|
|
132
110
|
|
|
133
111
|
if (args.from) {
|
|
134
|
-
emptyMsg = `No activity from @${args.from
|
|
112
|
+
emptyMsg = `No activity from @${normalizeHandle(args.from)} yet`;
|
|
135
113
|
suggestion = `Try "feed" to see all activity`;
|
|
136
114
|
}
|
|
137
115
|
|
|
@@ -146,7 +124,7 @@ async function handler(args) {
|
|
|
146
124
|
display += ` (${args.filter})`;
|
|
147
125
|
}
|
|
148
126
|
if (args.from) {
|
|
149
|
-
display += ` @${args.from
|
|
127
|
+
display += ` @${normalizeHandle(args.from)}`;
|
|
150
128
|
}
|
|
151
129
|
if (args.tag) {
|
|
152
130
|
display += ` #${args.tag}`;
|
|
@@ -168,9 +146,7 @@ async function handler(args) {
|
|
|
168
146
|
display += `${typeInfo.emoji} **@${entry.author}**${meTag} ${typeInfo.verb}\n`;
|
|
169
147
|
|
|
170
148
|
// Content (truncated if long)
|
|
171
|
-
const truncatedContent = mainContent.length > 100
|
|
172
|
-
? mainContent.slice(0, 97) + '...'
|
|
173
|
-
: mainContent;
|
|
149
|
+
const truncatedContent = mainContent.length > 100 ? mainContent.slice(0, 97) + '...' : mainContent;
|
|
174
150
|
display += ` "${truncatedContent}"\n`;
|
|
175
151
|
|
|
176
152
|
// Metadata (URL, inspired by, etc.)
|
|
@@ -185,7 +161,15 @@ async function handler(args) {
|
|
|
185
161
|
// Tags (non-system ones)
|
|
186
162
|
if (entry.tags && entry.tags.length > 0) {
|
|
187
163
|
const visibleTags = entry.tags
|
|
188
|
-
.filter(
|
|
164
|
+
.filter(
|
|
165
|
+
t =>
|
|
166
|
+
!t.startsWith('inspired:') &&
|
|
167
|
+
!t.startsWith('fulfills:') &&
|
|
168
|
+
!t.startsWith('riff:') &&
|
|
169
|
+
!t.startsWith('claim:') &&
|
|
170
|
+
!t.startsWith('observation:') &&
|
|
171
|
+
!t.startsWith('type:')
|
|
172
|
+
)
|
|
189
173
|
.slice(0, 3);
|
|
190
174
|
if (visibleTags.length > 0) {
|
|
191
175
|
display += ` ${visibleTags.map(t => `#${t}`).join(' ')}\n`;
|
|
@@ -206,19 +190,7 @@ async function handler(args) {
|
|
|
206
190
|
display += ` _${timeAgo}_\n\n`;
|
|
207
191
|
});
|
|
208
192
|
|
|
209
|
-
|
|
210
|
-
const response = { display };
|
|
211
|
-
|
|
212
|
-
// Find the most recent ship to offer tip action for
|
|
213
|
-
const recentShip = entries.find(e => e.category === 'shipped');
|
|
214
|
-
if (recentShip && recentShip.author.toLowerCase() !== myHandle.toLowerCase()) {
|
|
215
|
-
response.actions = formatActions(actions.afterShipView(recentShip));
|
|
216
|
-
response.hint = 'tip_ship_available';
|
|
217
|
-
response.ship_author = recentShip.author;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return response;
|
|
221
|
-
|
|
193
|
+
return { display };
|
|
222
194
|
} catch (error) {
|
|
223
195
|
return { display: `⚠️ Failed to load feed: ${error.message}` };
|
|
224
196
|
}
|
package/tools/follow.js
CHANGED
|
@@ -1,224 +1,101 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* vibe follow — Follow
|
|
2
|
+
* vibe follow/unfollow — Follow and unfollow creators
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - unfollow @handle → Unfollow a creator
|
|
7
|
-
* - following → See who you follow (+ their live status)
|
|
8
|
-
* - followers → See who follows you
|
|
4
|
+
* vibe_follow: Follow a creator to see their updates
|
|
5
|
+
* vibe_unfollow: Unfollow a creator
|
|
9
6
|
*/
|
|
10
7
|
|
|
8
|
+
const { requireInit, normalizeHandle } = require('./_shared');
|
|
11
9
|
const config = require('../config');
|
|
12
|
-
const
|
|
10
|
+
const store = require('../store');
|
|
13
11
|
|
|
14
|
-
const
|
|
12
|
+
const followDefinition = {
|
|
15
13
|
name: 'vibe_follow',
|
|
16
|
-
description: 'Follow
|
|
14
|
+
description: 'Follow a creator to see their updates, ships, and sessions.',
|
|
17
15
|
inputSchema: {
|
|
18
16
|
type: 'object',
|
|
19
17
|
properties: {
|
|
20
|
-
|
|
18
|
+
handle: {
|
|
21
19
|
type: 'string',
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
description: 'Who to follow (e.g., @stan)'
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
required: ['handle']
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const unfollowDefinition = {
|
|
28
|
+
name: 'vibe_unfollow',
|
|
29
|
+
description: 'Unfollow a creator.',
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
properties: {
|
|
25
33
|
handle: {
|
|
26
34
|
type: 'string',
|
|
27
|
-
description: '
|
|
35
|
+
description: 'Who to unfollow (e.g., @stan)'
|
|
28
36
|
}
|
|
29
|
-
}
|
|
37
|
+
},
|
|
38
|
+
required: ['handle']
|
|
30
39
|
}
|
|
31
40
|
};
|
|
32
41
|
|
|
33
|
-
async function
|
|
42
|
+
async function followHandler(args) {
|
|
34
43
|
const initCheck = requireInit();
|
|
35
44
|
if (initCheck) return initCheck;
|
|
36
45
|
|
|
37
46
|
const myHandle = config.getHandle();
|
|
38
|
-
const
|
|
39
|
-
const action = args.action || 'following';
|
|
40
|
-
const targetHandle = args.handle?.replace('@', '').toLowerCase();
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
// FOLLOW
|
|
44
|
-
if (action === 'follow') {
|
|
45
|
-
if (!targetHandle) {
|
|
46
|
-
return { display: '⚠️ Specify who to follow: `vibe follow @handle`' };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const response = await fetch(`${apiUrl}/api/follow`, {
|
|
50
|
-
method: 'POST',
|
|
51
|
-
headers: { 'Content-Type': 'application/json' },
|
|
52
|
-
body: JSON.stringify({
|
|
53
|
-
follower: myHandle,
|
|
54
|
-
following: targetHandle
|
|
55
|
-
})
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const data = await response.json();
|
|
59
|
-
|
|
60
|
-
if (!data.success) {
|
|
61
|
-
return { display: `⚠️ ${data.error}` };
|
|
62
|
-
}
|
|
47
|
+
const them = normalizeHandle(args.handle);
|
|
63
48
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
display += `_You follow ${data.counts.youAreFollowing} creators_`;
|
|
68
|
-
return { display };
|
|
69
|
-
} else {
|
|
70
|
-
return { display: `Already following @${targetHandle}` };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// UNFOLLOW
|
|
75
|
-
if (action === 'unfollow') {
|
|
76
|
-
if (!targetHandle) {
|
|
77
|
-
return { display: '⚠️ Specify who to unfollow: `vibe unfollow @handle`' };
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const response = await fetch(`${apiUrl}/api/follow`, {
|
|
81
|
-
method: 'DELETE',
|
|
82
|
-
headers: { 'Content-Type': 'application/json' },
|
|
83
|
-
body: JSON.stringify({
|
|
84
|
-
follower: myHandle,
|
|
85
|
-
following: targetHandle
|
|
86
|
-
})
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const data = await response.json();
|
|
90
|
-
|
|
91
|
-
if (!data.success) {
|
|
92
|
-
return { display: `⚠️ ${data.error}` };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let display = `✅ Unfollowed **@${targetHandle}**\n\n`;
|
|
96
|
-
display += `_You follow ${data.counts.youAreFollowing} creators_`;
|
|
97
|
-
return { display };
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// FOLLOWING (who I follow)
|
|
101
|
-
if (action === 'following') {
|
|
102
|
-
const response = await fetch(`${apiUrl}/api/following?handle=${myHandle}`);
|
|
103
|
-
const data = await response.json();
|
|
104
|
-
|
|
105
|
-
if (!data.success) {
|
|
106
|
-
return { display: `⚠️ ${data.error}` };
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (data.following.length === 0) {
|
|
110
|
-
let display = `## Following\n\n`;
|
|
111
|
-
display += `_You're not following anyone yet._\n\n`;
|
|
112
|
-
display += `**Discover creators:**\n`;
|
|
113
|
-
display += `- \`vibe discover\` — Find recommended creators\n`;
|
|
114
|
-
display += `- \`vibe follow @handle\` — Follow someone`;
|
|
115
|
-
return { display };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
let display = `## Following (${data.count})\n\n`;
|
|
119
|
-
|
|
120
|
-
// Group by status
|
|
121
|
-
const live = data.following.filter(f => f.isLive);
|
|
122
|
-
const scheduled = data.following.filter(f => !f.isLive && f.nextScheduled);
|
|
123
|
-
const other = data.following.filter(f => !f.isLive && !f.nextScheduled);
|
|
124
|
-
|
|
125
|
-
if (live.length > 0) {
|
|
126
|
-
display += `**🔴 Live Now**\n`;
|
|
127
|
-
for (const f of live) {
|
|
128
|
-
display += `• **@${f.handle}** — [Watch](https://slashvibe.dev/watch/${f.roomId})\n`;
|
|
129
|
-
}
|
|
130
|
-
display += `\n`;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (scheduled.length > 0) {
|
|
134
|
-
display += `**📅 Upcoming**\n`;
|
|
135
|
-
for (const f of scheduled) {
|
|
136
|
-
const when = formatScheduledTime(f.nextScheduled.scheduledFor);
|
|
137
|
-
display += `• **@${f.handle}** — ${f.nextScheduled.title} (${when})\n`;
|
|
138
|
-
}
|
|
139
|
-
display += `\n`;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (other.length > 0) {
|
|
143
|
-
display += `**Following**\n`;
|
|
144
|
-
for (const f of other) {
|
|
145
|
-
const followers = f.followerCount > 0 ? ` (${f.followerCount} followers)` : '';
|
|
146
|
-
display += `• @${f.handle}${followers}\n`;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return { display };
|
|
151
|
-
}
|
|
49
|
+
if (them === myHandle) {
|
|
50
|
+
return { display: "You can't follow yourself." };
|
|
51
|
+
}
|
|
152
52
|
|
|
153
|
-
|
|
154
|
-
if (action === 'followers') {
|
|
155
|
-
const response = await fetch(`${apiUrl}/api/followers?handle=${myHandle}`);
|
|
156
|
-
const data = await response.json();
|
|
53
|
+
const result = await store.followUser(myHandle, them);
|
|
157
54
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
55
|
+
if (result.success === false) {
|
|
56
|
+
return { display: `Failed to follow @${them}: ${result.error || 'Unknown error'}` };
|
|
57
|
+
}
|
|
161
58
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
display += `**Build your audience:**\n`;
|
|
166
|
-
display += `- Go live: \`vibe broadcast start\`\n`;
|
|
167
|
-
display += `- Share what you're building\n`;
|
|
168
|
-
display += `- Engage with other creators`;
|
|
169
|
-
return { display };
|
|
170
|
-
}
|
|
59
|
+
if (result.message === 'Already following') {
|
|
60
|
+
return { display: `You're already following **@${them}**.` };
|
|
61
|
+
}
|
|
171
62
|
|
|
172
|
-
|
|
63
|
+
const counts = result.counts || {};
|
|
64
|
+
let display = `Now following **@${them}**`;
|
|
65
|
+
if (counts.youAreFollowing) {
|
|
66
|
+
display += ` (following ${counts.youAreFollowing} people)`;
|
|
67
|
+
}
|
|
173
68
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const inactive = data.followers.filter(f => !f.isActive);
|
|
69
|
+
return { display };
|
|
70
|
+
}
|
|
177
71
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const building = f.building ? ` — ${f.building}` : '';
|
|
182
|
-
display += `• @${f.handle}${building}\n`;
|
|
183
|
-
}
|
|
184
|
-
display += `\n`;
|
|
185
|
-
}
|
|
72
|
+
async function unfollowHandler(args) {
|
|
73
|
+
const initCheck = requireInit();
|
|
74
|
+
if (initCheck) return initCheck;
|
|
186
75
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
for (const f of inactive.slice(0, 20)) {
|
|
190
|
-
display += `• @${f.handle}\n`;
|
|
191
|
-
}
|
|
192
|
-
if (inactive.length > 20) {
|
|
193
|
-
display += `_...and ${inactive.length - 20} more_\n`;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
76
|
+
const myHandle = config.getHandle();
|
|
77
|
+
const them = normalizeHandle(args.handle);
|
|
196
78
|
|
|
197
|
-
|
|
198
|
-
}
|
|
79
|
+
if (them === myHandle) {
|
|
80
|
+
return { display: "You can't unfollow yourself." };
|
|
81
|
+
}
|
|
199
82
|
|
|
200
|
-
|
|
83
|
+
const result = await store.unfollowUser(myHandle, them);
|
|
201
84
|
|
|
202
|
-
|
|
203
|
-
return { display:
|
|
85
|
+
if (result.success === false) {
|
|
86
|
+
return { display: `Failed to unfollow @${them}: ${result.error || 'Unknown error'}` };
|
|
204
87
|
}
|
|
205
|
-
}
|
|
206
88
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
212
|
-
const diffDays = Math.floor(diffHours / 24);
|
|
213
|
-
|
|
214
|
-
if (diffDays === 0) {
|
|
215
|
-
if (diffHours <= 1) return 'in < 1 hour';
|
|
216
|
-
return `in ${diffHours} hours`;
|
|
217
|
-
} else if (diffDays === 1) {
|
|
218
|
-
return 'tomorrow';
|
|
219
|
-
} else {
|
|
220
|
-
return `in ${diffDays} days`;
|
|
89
|
+
const counts = result.counts || {};
|
|
90
|
+
let display = `Unfollowed **@${them}**`;
|
|
91
|
+
if (counts.youAreFollowing != null) {
|
|
92
|
+
display += ` (following ${counts.youAreFollowing} people)`;
|
|
221
93
|
}
|
|
94
|
+
|
|
95
|
+
return { display };
|
|
222
96
|
}
|
|
223
97
|
|
|
224
|
-
module.exports = {
|
|
98
|
+
module.exports = {
|
|
99
|
+
follow: { definition: followDefinition, handler: followHandler },
|
|
100
|
+
unfollow: { definition: unfollowDefinition, handler: unfollowHandler }
|
|
101
|
+
};
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* vibe forget --all — Delete all memories (requires confirmation)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
const
|
|
12
|
+
const { requireInit, normalizeHandle } = require('./_shared');
|
|
13
13
|
const memory = require('../memory');
|
|
14
14
|
const fs = require('fs');
|
|
15
15
|
|
|
@@ -36,11 +36,8 @@ const definition = {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
async function handler(args) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
display: 'Run `vibe init` first to set your identity.'
|
|
42
|
-
};
|
|
43
|
-
}
|
|
39
|
+
const initCheck = requireInit();
|
|
40
|
+
if (initCheck) return initCheck;
|
|
44
41
|
|
|
45
42
|
const { all, confirm } = args;
|
|
46
43
|
let { handle } = args;
|
|
@@ -93,7 +90,7 @@ async function handler(args) {
|
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
// Clean handle
|
|
96
|
-
handle = handle
|
|
93
|
+
handle = normalizeHandle(handle);
|
|
97
94
|
|
|
98
95
|
// Check if thread exists
|
|
99
96
|
const countBefore = memory.count(handle);
|