slashvibe-mcp 0.2.8 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -58
- package/analytics.js +107 -0
- package/auth-store.js +148 -0
- package/auto-update.js +130 -0
- package/bridges/bridge-monitor.js +388 -0
- package/bridges/discord-bot.js +431 -0
- package/bridges/farcaster.js +299 -0
- package/bridges/telegram.js +261 -0
- package/bridges/webhook-health.js +420 -0
- package/bridges/webhook-server.js +437 -0
- package/bridges/whatsapp.js +441 -0
- package/bridges/x-webhook.js +423 -0
- package/config.js +27 -15
- package/games/arcade.js +406 -0
- package/games/chess.js +451 -0
- package/games/colorguess.js +343 -0
- package/games/crossword-words.js +171 -0
- package/games/crossword.js +461 -0
- package/games/drawing.js +347 -0
- package/games/gameroulette.js +300 -0
- package/games/gamerouter.js +336 -0
- package/games/gamestatus.js +337 -0
- package/games/guessnumber.js +209 -0
- package/games/hangman.js +279 -0
- package/games/memory.js +338 -0
- package/games/multiplayer-tictactoe.js +389 -0
- package/games/pixelart.js +399 -0
- package/games/quickduel.js +354 -0
- package/games/riddle.js +371 -0
- package/games/rockpaperscissors.js +291 -0
- package/games/snake.js +406 -0
- package/games/storybuilder.js +343 -0
- package/games/tictactoe.js +345 -0
- package/games/twentyquestions.js +286 -0
- package/games/twotruths.js +207 -0
- package/games/werewolf.js +508 -0
- package/games/wordassociation.js +247 -0
- package/games/wordchain.js +135 -0
- package/index.js +77 -53
- package/intelligence/index.js +9 -2
- package/intelligence/interests.js +369 -0
- package/notification-emitter.js +77 -0
- package/notify.js +5 -1
- package/package.json +18 -6
- package/prompts.js +1 -1
- package/protocol/index.js +73 -0
- package/setup.js +402 -0
- package/store/api.js +436 -211
- package/store/profiles.js +160 -12
- package/tools/_actions.js +362 -21
- package/tools/_discovery.js +119 -26
- package/tools/_shared/index.js +64 -0
- package/tools/_shared.js +234 -0
- package/tools/_work-context.js +338 -0
- package/tools/_work-context.manual-test.js +199 -0
- package/tools/_work-context.test.js +260 -0
- package/tools/activity.js +220 -0
- package/tools/analytics.js +191 -0
- package/tools/approve.js +197 -0
- package/tools/artifact-create.js +14 -3
- package/tools/artifacts-price.js +107 -0
- package/tools/available.js +120 -0
- package/tools/broadcast.js +286 -0
- package/tools/chat.js +202 -0
- package/tools/collaborative-drawing.js +1 -1
- package/tools/connection-status.js +178 -0
- package/tools/discover.js +350 -34
- package/tools/dm.js +80 -8
- package/tools/earnings.js +126 -0
- package/tools/feed.js +35 -4
- package/tools/follow.js +224 -0
- package/tools/friends.js +207 -0
- package/tools/gig-browse.js +206 -0
- package/tools/gig-complete.js +144 -0
- package/tools/help.js +3 -3
- package/tools/idea.js +9 -2
- package/tools/inbox.js +289 -105
- package/tools/init.js +106 -27
- package/tools/invite.js +15 -4
- package/tools/migrate.js +3 -3
- package/tools/multiplayer-game.js +1 -1
- package/tools/onboarding.js +7 -7
- package/tools/open.js +143 -12
- package/tools/party-game.js +1 -1
- package/tools/plan.js +225 -0
- package/tools/proof-of-work.js +144 -0
- package/tools/reply.js +166 -0
- package/tools/report.js +1 -1
- package/tools/request.js +17 -3
- package/tools/schedule.js +367 -0
- package/tools/search-messages.js +123 -0
- package/tools/session.js +420 -0
- package/tools/session_price.js +128 -0
- package/tools/settings.js +90 -2
- package/tools/ship.js +30 -7
- package/tools/smart-check.js +201 -0
- package/tools/start.js +147 -12
- package/tools/status.js +53 -6
- package/tools/stuck.js +297 -0
- package/tools/subscribe.js +148 -0
- package/tools/subscriptions.js +134 -0
- package/tools/suggest-tags.js +6 -8
- package/tools/tag-suggestions.js +1 -1
- package/tools/tip.js +150 -77
- package/tools/token.js +4 -4
- package/tools/update.js +1 -1
- package/tools/wallet.js +221 -79
- package/tools/watch.js +157 -0
- package/tools/who.js +30 -1
- package/tools/withdraw.js +145 -0
- package/tools/work-summary.js +96 -0
- package/version.json +10 -8
- package/LICENSE +0 -21
- package/store/sqlite.js +0 -347
- /package/tools/{auto-suggest-connections.js → _deprecated/auto-suggest-connections.js} +0 -0
- /package/tools/{away.js → _deprecated/away.js} +0 -0
- /package/tools/{back.js → _deprecated/back.js} +0 -0
- /package/tools/{bootstrap-skills.js → _deprecated/bootstrap-skills.js} +0 -0
- /package/tools/{bridge-dashboard.js → _deprecated/bridge-dashboard.js} +0 -0
- /package/tools/{bridge-health.js → _deprecated/bridge-health.js} +0 -0
- /package/tools/{bridge-live.js → _deprecated/bridge-live.js} +0 -0
- /package/tools/{bridges.js → _deprecated/bridges.js} +0 -0
- /package/tools/{colorguess.js → _deprecated/colorguess.js} +0 -0
- /package/tools/{discover-insights.js → _deprecated/discover-insights.js} +0 -0
- /package/tools/{discover-momentum.js → _deprecated/discover-momentum.js} +0 -0
- /package/tools/{discovery-analytics.js → _deprecated/discovery-analytics.js} +0 -0
- /package/tools/{discovery-auto-suggest.js → _deprecated/discovery-auto-suggest.js} +0 -0
- /package/tools/{discovery-bootstrap.js → _deprecated/discovery-bootstrap.js} +0 -0
- /package/tools/{discovery-daily.js → _deprecated/discovery-daily.js} +0 -0
- /package/tools/{discovery-dashboard.js → _deprecated/discovery-dashboard.js} +0 -0
- /package/tools/{discovery-digest.js → _deprecated/discovery-digest.js} +0 -0
- /package/tools/{discovery-hub.js → _deprecated/discovery-hub.js} +0 -0
- /package/tools/{discovery-insights.js → _deprecated/discovery-insights.js} +0 -0
- /package/tools/{discovery-momentum.js → _deprecated/discovery-momentum.js} +0 -0
- /package/tools/{discovery-monitor.js → _deprecated/discovery-monitor.js} +0 -0
- /package/tools/{discovery-proactive.js → _deprecated/discovery-proactive.js} +0 -0
- /package/tools/{draw.js → _deprecated/draw.js} +0 -0
- /package/tools/{farcaster.js → _deprecated/farcaster.js} +0 -0
- /package/tools/{forget.js → _deprecated/forget.js} +0 -0
- /package/tools/{games-catalog.js → _deprecated/games-catalog.js} +0 -0
- /package/tools/{games.js → _deprecated/games.js} +0 -0
- /package/tools/{guessnumber.js → _deprecated/guessnumber.js} +0 -0
- /package/tools/{hangman.js → _deprecated/hangman.js} +0 -0
- /package/tools/{multiplayer-tictactoe.js → _deprecated/multiplayer-tictactoe.js} +0 -0
- /package/tools/{mute.js → _deprecated/mute.js} +0 -0
- /package/tools/{recall.js → _deprecated/recall.js} +0 -0
- /package/tools/{remember.js → _deprecated/remember.js} +0 -0
- /package/tools/{riddle.js → _deprecated/riddle.js} +0 -0
- /package/tools/{run-bootstrap.js → _deprecated/run-bootstrap.js} +0 -0
- /package/tools/{skills-analytics.js → _deprecated/skills-analytics.js} +0 -0
- /package/tools/{skills-bootstrap.js → _deprecated/skills-bootstrap.js} +0 -0
- /package/tools/{skills-dashboard.js → _deprecated/skills-dashboard.js} +0 -0
- /package/tools/{skills-exchange.js → _deprecated/skills-exchange.js} +0 -0
- /package/tools/{skills.js → _deprecated/skills.js} +0 -0
- /package/tools/{smart-intro.js → _deprecated/smart-intro.js} +0 -0
- /package/tools/{storybuilder.js → _deprecated/storybuilder.js} +0 -0
- /package/tools/{telegram-bot.js → _deprecated/telegram-bot.js} +0 -0
- /package/tools/{telegram-setup.js → _deprecated/telegram-setup.js} +0 -0
- /package/tools/{tictactoe.js → _deprecated/tictactoe.js} +0 -0
- /package/tools/{twentyquestions.js → _deprecated/twentyquestions.js} +0 -0
- /package/tools/{wordassociation.js → _deprecated/wordassociation.js} +0 -0
package/tools/session.js
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe session — Session management
|
|
3
|
+
*
|
|
4
|
+
* Save broadcasts as replayable sessions, browse sessions, and fork them.
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* - session save → Save current/recent broadcast as session
|
|
8
|
+
* - session save --room X → Save specific broadcast
|
|
9
|
+
* - session list → List my sessions
|
|
10
|
+
* - session browse → Browse all public sessions
|
|
11
|
+
* - session fork <id> → Fork a session
|
|
12
|
+
* - session view <id> → View session details
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const config = require('../config');
|
|
16
|
+
|
|
17
|
+
const definition = {
|
|
18
|
+
name: 'vibe_session',
|
|
19
|
+
description: 'Save broadcasts as sessions, list your sessions, fork others\' sessions.',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
action: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
enum: ['save', 'list', 'browse', 'fork', 'view'],
|
|
26
|
+
description: 'Action: save, list, browse, fork, or view'
|
|
27
|
+
},
|
|
28
|
+
room: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: 'Room ID to save (for save action)'
|
|
31
|
+
},
|
|
32
|
+
id: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Session ID (for fork/view action)'
|
|
35
|
+
},
|
|
36
|
+
title: {
|
|
37
|
+
type: 'string',
|
|
38
|
+
description: 'Session title (for save action)'
|
|
39
|
+
},
|
|
40
|
+
visibility: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
enum: ['public', 'unlisted', 'private'],
|
|
43
|
+
description: 'Session visibility (default: public)'
|
|
44
|
+
},
|
|
45
|
+
limit: {
|
|
46
|
+
type: 'number',
|
|
47
|
+
description: 'Number of sessions to show (default: 10)'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
async function handler(args) {
|
|
54
|
+
const myHandle = config.getHandle();
|
|
55
|
+
const apiUrl = config.getApiUrl();
|
|
56
|
+
const action = args.action || 'list';
|
|
57
|
+
|
|
58
|
+
if (!myHandle && action !== 'browse' && action !== 'view') {
|
|
59
|
+
return { display: '⚠️ Not initialized. Run `vibe init` first.' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// SAVE broadcast as session
|
|
63
|
+
if (action === 'save') {
|
|
64
|
+
// Get room ID from args or from active broadcast
|
|
65
|
+
let roomId = args.room;
|
|
66
|
+
|
|
67
|
+
if (!roomId) {
|
|
68
|
+
// Check for active broadcast
|
|
69
|
+
const broadcast = config.get('broadcast');
|
|
70
|
+
if (broadcast) {
|
|
71
|
+
roomId = broadcast.roomId;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!roomId) {
|
|
76
|
+
return {
|
|
77
|
+
display: `⚠️ No broadcast to save.\n\n**Options:**\n1. Specify a room: \`vibe session save --room room_xxx\`\n2. Start a broadcast first: \`vibe broadcast start\``
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const title = args.title || `Session by @${myHandle}`;
|
|
82
|
+
const visibility = args.visibility || 'public';
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const response = await fetch(`${apiUrl}/api/sessions`, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
fromBroadcast: roomId,
|
|
90
|
+
handle: myHandle,
|
|
91
|
+
title,
|
|
92
|
+
visibility
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
|
|
98
|
+
if (!data.success) {
|
|
99
|
+
if (data.error?.includes('buffer not found')) {
|
|
100
|
+
return {
|
|
101
|
+
display: `⚠️ Broadcast buffer expired.\n\n_Buffers expire 1 hour after broadcast ends. Start a new broadcast to create a session._`
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return { display: `⚠️ Failed to save session: ${data.error}` };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const formatDuration = (secs) => {
|
|
108
|
+
if (!secs || secs < 60) return 'under 1 minute';
|
|
109
|
+
if (secs < 3600) return `${Math.floor(secs / 60)} minutes`;
|
|
110
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Chapter icons for display
|
|
114
|
+
const chapterIcons = {
|
|
115
|
+
setup: '🔧',
|
|
116
|
+
problem: '❌',
|
|
117
|
+
investigation: '🔍',
|
|
118
|
+
breakthrough: '💡',
|
|
119
|
+
implementation: '⚡',
|
|
120
|
+
ship: '🚀'
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
let display = `✅ **Session Saved**\n\n`;
|
|
124
|
+
display += `**${data.title}**\n\n`;
|
|
125
|
+
display += `📎 ${data.shareUrl}\n\n`;
|
|
126
|
+
display += `**Stats:**\n`;
|
|
127
|
+
display += `- Duration: ${formatDuration(data.stats?.durationSeconds)}\n`;
|
|
128
|
+
display += `- Chunks: ${data.stats?.chunks || 0}\n`;
|
|
129
|
+
|
|
130
|
+
// Show chapters if detected
|
|
131
|
+
const chapters = data.chapters || [];
|
|
132
|
+
if (chapters.length > 0) {
|
|
133
|
+
display += `- Chapters: ${chapters.length}\n\n`;
|
|
134
|
+
display += `📖 **Story Arc:**\n`;
|
|
135
|
+
const chapterFlow = chapters.map(ch => {
|
|
136
|
+
const icon = chapterIcons[ch.type] || '📋';
|
|
137
|
+
return `${icon} ${ch.title}`;
|
|
138
|
+
}).join(' → ');
|
|
139
|
+
display += ` ${chapterFlow}\n\n`;
|
|
140
|
+
} else {
|
|
141
|
+
display += `- Fork points: ${data.stats?.forkPoints || 0}\n\n`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
display += `_Share this URL or find it later with \`vibe session list\`_`;
|
|
145
|
+
|
|
146
|
+
return { display };
|
|
147
|
+
|
|
148
|
+
} catch (error) {
|
|
149
|
+
return { display: `## Save Error\n\n${error.message}` };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// LIST my sessions
|
|
154
|
+
if (action === 'list') {
|
|
155
|
+
const limit = args.limit || 10;
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const response = await fetch(
|
|
159
|
+
`${apiUrl}/api/sessions/browse?author=${myHandle}&limit=${limit}`,
|
|
160
|
+
{ headers: { 'Accept': 'application/json' } }
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const data = await response.json();
|
|
164
|
+
|
|
165
|
+
if (!data.success) {
|
|
166
|
+
return { display: `⚠️ Failed to fetch sessions: ${data.error}` };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!data.sessions || data.sessions.length === 0) {
|
|
170
|
+
let display = `📼 **No Sessions Yet**\n\n`;
|
|
171
|
+
display += `You haven't saved any sessions.\n\n`;
|
|
172
|
+
display += `**Create your first session:**\n`;
|
|
173
|
+
display += `1. \`vibe broadcast start "What you're building"\`\n`;
|
|
174
|
+
display += `2. Code for a while...\n`;
|
|
175
|
+
display += `3. \`vibe broadcast stop\`\n`;
|
|
176
|
+
display += `4. \`vibe session save\``;
|
|
177
|
+
return { display };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const formatDuration = (secs) => {
|
|
181
|
+
if (!secs || secs < 60) return '<1m';
|
|
182
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m`;
|
|
183
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const formatDate = (dateStr) => {
|
|
187
|
+
const date = new Date(dateStr);
|
|
188
|
+
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
let display = `📼 **Your Sessions** (${data.sessions.length}${data.hasMore ? '+' : ''})\n\n`;
|
|
192
|
+
|
|
193
|
+
for (const s of data.sessions) {
|
|
194
|
+
const duration = formatDuration(s.duration_seconds);
|
|
195
|
+
const date = formatDate(s.created_at);
|
|
196
|
+
const views = s.views || 0;
|
|
197
|
+
const forks = s.forks || 0;
|
|
198
|
+
|
|
199
|
+
display += `**${s.title}**\n`;
|
|
200
|
+
display += ` ${date} · ${duration}`;
|
|
201
|
+
if (views > 0) display += ` · ${views} views`;
|
|
202
|
+
if (forks > 0) display += ` · ${forks} forks`;
|
|
203
|
+
display += `\n`;
|
|
204
|
+
display += ` → \`vibe session view ${s.id}\`\n\n`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (data.hasMore) {
|
|
208
|
+
display += `_More sessions available. Use \`--limit 20\` to see more._`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return { display };
|
|
212
|
+
|
|
213
|
+
} catch (error) {
|
|
214
|
+
return { display: `## List Error\n\n${error.message}` };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// BROWSE all public sessions
|
|
219
|
+
if (action === 'browse') {
|
|
220
|
+
const limit = args.limit || 10;
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const response = await fetch(
|
|
224
|
+
`${apiUrl}/api/sessions/browse?limit=${limit}&sort=popular`,
|
|
225
|
+
{ headers: { 'Accept': 'application/json' } }
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const data = await response.json();
|
|
229
|
+
|
|
230
|
+
if (!data.success) {
|
|
231
|
+
return { display: `⚠️ Failed to fetch sessions: ${data.error}` };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (!data.sessions || data.sessions.length === 0) {
|
|
235
|
+
return {
|
|
236
|
+
display: `📼 No public sessions yet.\n\n_Be the first! Start a broadcast and save it._`
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const formatDuration = (secs) => {
|
|
241
|
+
if (!secs || secs < 60) return '<1m';
|
|
242
|
+
if (secs < 3600) return `${Math.floor(secs / 60)}m`;
|
|
243
|
+
return `${Math.floor(secs / 3600)}h`;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
let display = `📼 **Public Sessions** (${data.total} total)\n\n`;
|
|
247
|
+
|
|
248
|
+
for (const s of data.sessions) {
|
|
249
|
+
const duration = formatDuration(s.duration_seconds);
|
|
250
|
+
const views = s.views || 0;
|
|
251
|
+
const forks = s.forks || 0;
|
|
252
|
+
|
|
253
|
+
display += `**${s.title}** by @${s.author_handle}\n`;
|
|
254
|
+
display += ` ${duration}`;
|
|
255
|
+
if (views > 0) display += ` · ${views} views`;
|
|
256
|
+
if (forks > 0) display += ` · ${forks} forks`;
|
|
257
|
+
display += `\n`;
|
|
258
|
+
display += ` → \`vibe session view ${s.id}\`\n\n`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (data.hasMore) {
|
|
262
|
+
display += `_More sessions available. Use \`--limit 20\` to see more._`;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return { display };
|
|
266
|
+
|
|
267
|
+
} catch (error) {
|
|
268
|
+
return { display: `## Browse Error\n\n${error.message}` };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// VIEW session details
|
|
273
|
+
if (action === 'view') {
|
|
274
|
+
if (!args.id) {
|
|
275
|
+
return { display: '⚠️ Session ID required. Usage: `vibe session view ses_xxx`' };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const sessionId = args.id;
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
// Use the story endpoint which returns full session details
|
|
282
|
+
const response = await fetch(`${apiUrl}/story/${sessionId}?format=json`);
|
|
283
|
+
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
return {
|
|
286
|
+
display: `⚠️ Session not found: ${sessionId}\n\n_Check the ID or browse sessions with \`vibe session browse\`_`
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const data = await response.json();
|
|
291
|
+
|
|
292
|
+
if (!data.success && !data.session) {
|
|
293
|
+
return { display: `⚠️ Failed to fetch session: ${data.error}` };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const s = data.session || data.story || data;
|
|
297
|
+
|
|
298
|
+
const formatDuration = (secs) => {
|
|
299
|
+
if (!secs || secs < 60) return 'under 1 minute';
|
|
300
|
+
if (secs < 3600) return `${Math.floor(secs / 60)} minutes`;
|
|
301
|
+
return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Chapter icons for display
|
|
305
|
+
const chapterIcons = {
|
|
306
|
+
setup: '🔧',
|
|
307
|
+
problem: '❌',
|
|
308
|
+
investigation: '🔍',
|
|
309
|
+
breakthrough: '💡',
|
|
310
|
+
implementation: '⚡',
|
|
311
|
+
ship: '🚀'
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
let display = `📼 **${s.title}**\n\n`;
|
|
315
|
+
display += `**Author:** @${s.author || s.author_handle}\n`;
|
|
316
|
+
display += `**Duration:** ${formatDuration(s.durationSeconds || s.duration_seconds)}\n`;
|
|
317
|
+
display += `**Views:** ${s.views || 0}\n`;
|
|
318
|
+
display += `**Forks:** ${s.forks || 0}\n\n`;
|
|
319
|
+
|
|
320
|
+
// Display chapters if available
|
|
321
|
+
const chapters = s.chapters || [];
|
|
322
|
+
if (chapters.length > 0) {
|
|
323
|
+
display += `📖 **Chapters** (${chapters.length})\n`;
|
|
324
|
+
for (let i = 0; i < chapters.length; i++) {
|
|
325
|
+
const ch = chapters[i];
|
|
326
|
+
const icon = chapterIcons[ch.type] || '📋';
|
|
327
|
+
display += ` ${i + 1}. ${icon} **${ch.title}** (${ch.startTime || '0:00'})\n`;
|
|
328
|
+
if (ch.summary) {
|
|
329
|
+
display += ` _${ch.summary.slice(0, 60)}${ch.summary.length > 60 ? '...' : ''}_\n`;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
display += '\n';
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Display fork points if available
|
|
336
|
+
const forkPoints = s.forkPoints || [];
|
|
337
|
+
if (forkPoints.length > 0) {
|
|
338
|
+
display += `🔀 **Fork Points** (${forkPoints.length})\n`;
|
|
339
|
+
for (const fp of forkPoints.slice(0, 5)) {
|
|
340
|
+
display += ` - ${fp.label}: ${fp.description || ''}\n`;
|
|
341
|
+
}
|
|
342
|
+
if (forkPoints.length > 5) {
|
|
343
|
+
display += ` _...and ${forkPoints.length - 5} more_\n`;
|
|
344
|
+
}
|
|
345
|
+
display += '\n';
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (s.description) {
|
|
349
|
+
display += `**Description:**\n${s.description}\n\n`;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (s.summary) {
|
|
353
|
+
display += `**Summary:**\n${s.summary}\n\n`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
display += `**View full session:**\n`;
|
|
357
|
+
display += `https://slashvibe.dev/story/${sessionId}\n\n`;
|
|
358
|
+
|
|
359
|
+
display += `**Actions:**\n`;
|
|
360
|
+
display += `- Fork: \`vibe session fork ${sessionId}\`\n`;
|
|
361
|
+
|
|
362
|
+
return { display };
|
|
363
|
+
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return { display: `## View Error\n\n${error.message}` };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// FORK a session
|
|
370
|
+
if (action === 'fork') {
|
|
371
|
+
if (!args.id) {
|
|
372
|
+
return { display: '⚠️ Session ID required. Usage: `vibe session fork ses_xxx`' };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const sessionId = args.id;
|
|
376
|
+
|
|
377
|
+
try {
|
|
378
|
+
const response = await fetch(`${apiUrl}/api/sessions/fork`, {
|
|
379
|
+
method: 'POST',
|
|
380
|
+
headers: { 'Content-Type': 'application/json' },
|
|
381
|
+
body: JSON.stringify({
|
|
382
|
+
parentSessionId: sessionId,
|
|
383
|
+
forkerHandle: myHandle,
|
|
384
|
+
forkTo: 'session',
|
|
385
|
+
forkLevel: 'story_only'
|
|
386
|
+
})
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const data = await response.json();
|
|
390
|
+
|
|
391
|
+
if (!data.success) {
|
|
392
|
+
if (data.error?.includes('not found')) {
|
|
393
|
+
return {
|
|
394
|
+
display: `⚠️ Session not found: ${sessionId}\n\n_Check the ID or browse sessions with \`vibe session browse\`_`
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
if (data.error?.includes('private')) {
|
|
398
|
+
return { display: `⚠️ Cannot fork a private session.` };
|
|
399
|
+
}
|
|
400
|
+
return { display: `⚠️ Failed to fork session: ${data.error}` };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let display = `🍴 **Session Forked!**\n\n`;
|
|
404
|
+
display += `**From:** ${data.parentSession?.title || sessionId}\n`;
|
|
405
|
+
display += `**By:** @${data.parentSession?.author || 'unknown'}\n\n`;
|
|
406
|
+
display += `**Fork ID:** ${data.forkId}\n\n`;
|
|
407
|
+
display += `**View the story:**\n${data.storyUrl}\n\n`;
|
|
408
|
+
display += `_Learn from this session and build your own version!_`;
|
|
409
|
+
|
|
410
|
+
return { display };
|
|
411
|
+
|
|
412
|
+
} catch (error) {
|
|
413
|
+
return { display: `## Fork Error\n\n${error.message}` };
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return { display: 'Unknown action. Use: save, list, browse, view, or fork' };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
module.exports = { definition, handler };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_session_price - Set session access type and price
|
|
3
|
+
*
|
|
4
|
+
* Configure how others can access your sessions:
|
|
5
|
+
* - public: Free for all
|
|
6
|
+
* - ppv: Pay-per-view ($1-$50)
|
|
7
|
+
* - subscribers_only: Requires active subscription
|
|
8
|
+
* - private: Creator only
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* - "set session to ppv $5"
|
|
12
|
+
* - "make my session public"
|
|
13
|
+
* - "set session price to $10"
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fetch = require('node-fetch');
|
|
17
|
+
const config = require('../config');
|
|
18
|
+
|
|
19
|
+
const definition = {
|
|
20
|
+
name: 'vibe_session_price',
|
|
21
|
+
description: 'Set session access type and price. Options: public (free), ppv ($1-$50), subscribers_only, private.',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
session_id: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Session ID to configure (defaults to current session if available)'
|
|
28
|
+
},
|
|
29
|
+
access_type: {
|
|
30
|
+
type: 'string',
|
|
31
|
+
enum: ['public', 'ppv', 'subscribers_only', 'private'],
|
|
32
|
+
description: 'Access type: public (free), ppv (pay-per-view), subscribers_only, private'
|
|
33
|
+
},
|
|
34
|
+
price_dollars: {
|
|
35
|
+
type: 'number',
|
|
36
|
+
description: 'Price in dollars for PPV access (1-50). Required if access_type is ppv.'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
required: ['access_type']
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
async function handler(args) {
|
|
44
|
+
const { session_id, access_type, price_dollars } = args;
|
|
45
|
+
|
|
46
|
+
if (!config.isInitialized()) {
|
|
47
|
+
return {
|
|
48
|
+
display: 'Run `vibe init` first to set your identity.'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Validate PPV pricing
|
|
53
|
+
if (access_type === 'ppv') {
|
|
54
|
+
if (!price_dollars || price_dollars < 1 || price_dollars > 50) {
|
|
55
|
+
return {
|
|
56
|
+
display: '❌ PPV access requires a price between $1 and $50'
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const token = config.getToken();
|
|
62
|
+
const apiUrl = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const response = await fetch(
|
|
66
|
+
`${apiUrl}/api/sessions/access`,
|
|
67
|
+
{
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
'Authorization': `Bearer ${token}`
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
session_id: session_id || undefined,
|
|
75
|
+
access_type,
|
|
76
|
+
ppv_price_dollars: price_dollars || undefined
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const result = await response.json();
|
|
82
|
+
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
return {
|
|
85
|
+
display: `❌ ${result.error || 'Failed to update session access'}`
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Format success message
|
|
90
|
+
let accessDesc;
|
|
91
|
+
switch (access_type) {
|
|
92
|
+
case 'public':
|
|
93
|
+
accessDesc = '🌐 Public - Free for everyone';
|
|
94
|
+
break;
|
|
95
|
+
case 'ppv':
|
|
96
|
+
accessDesc = `💵 Pay-Per-View - $${price_dollars}`;
|
|
97
|
+
break;
|
|
98
|
+
case 'subscribers_only':
|
|
99
|
+
accessDesc = '⭐ Subscribers Only';
|
|
100
|
+
break;
|
|
101
|
+
case 'private':
|
|
102
|
+
accessDesc = '🔒 Private - Only you';
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let display = `
|
|
107
|
+
✅ Session Access Updated
|
|
108
|
+
|
|
109
|
+
Session: ${result.session_id}
|
|
110
|
+
Access: ${accessDesc}`;
|
|
111
|
+
|
|
112
|
+
if (access_type === 'ppv') {
|
|
113
|
+
display += `\nPrice: $${price_dollars}`;
|
|
114
|
+
display += '\n\nViewers will need to purchase access to see this session.';
|
|
115
|
+
} else if (access_type === 'subscribers_only') {
|
|
116
|
+
display += '\n\nOnly your subscribers can view this session.';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { display: display.trim(), data: result };
|
|
120
|
+
|
|
121
|
+
} catch (error) {
|
|
122
|
+
return {
|
|
123
|
+
display: `❌ Failed to update session access: ${error.message}`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { definition, handler };
|
package/tools/settings.js
CHANGED
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
* - guided: on | off (dashboard mode)
|
|
7
7
|
* - github_activity: on | off (show GitHub shipping status)
|
|
8
8
|
* - github_activity_privacy: full | status_only | off
|
|
9
|
+
* - mute: 1h | 2h | 4h | forever | off (merged from vibe_mute)
|
|
10
|
+
* - auto_context: on | off (ambient work context gathering)
|
|
9
11
|
*/
|
|
10
12
|
|
|
11
13
|
const config = require('../config');
|
|
12
14
|
|
|
13
15
|
const definition = {
|
|
14
16
|
name: 'vibe_settings',
|
|
15
|
-
description: 'Configure /vibe preferences. Set notification level, toggle guided mode,
|
|
17
|
+
description: 'Configure /vibe preferences. Set notification level, toggle guided mode, enable GitHub activity signals, or mute alerts.',
|
|
16
18
|
inputSchema: {
|
|
17
19
|
type: 'object',
|
|
18
20
|
properties: {
|
|
@@ -33,6 +35,14 @@ const definition = {
|
|
|
33
35
|
type: 'string',
|
|
34
36
|
enum: ['full', 'status_only', 'off'],
|
|
35
37
|
description: 'Privacy level: full (repos/commits), status_only (just badge), off'
|
|
38
|
+
},
|
|
39
|
+
mute: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'Mute presence alerts: "1h", "2h", "4h", "forever", or "off" to unmute'
|
|
42
|
+
},
|
|
43
|
+
auto_context: {
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
description: 'Enable/disable automatic work context gathering (git branch, recent commits shown in presence)'
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
48
|
}
|
|
@@ -65,12 +75,68 @@ async function handler(args) {
|
|
|
65
75
|
changes.push('github activity privacy → **' + args.github_activity_privacy + '**');
|
|
66
76
|
}
|
|
67
77
|
|
|
78
|
+
// Handle mute setting (merged from vibe_mute)
|
|
79
|
+
if (args.mute) {
|
|
80
|
+
const duration = args.mute;
|
|
81
|
+
|
|
82
|
+
// UNMUTE
|
|
83
|
+
if (duration === 'off' || duration === 'unmute') {
|
|
84
|
+
config.set('mutedUntil', null);
|
|
85
|
+
config.set('focusMode', false);
|
|
86
|
+
changes.push('alerts → **unmuted**');
|
|
87
|
+
}
|
|
88
|
+
// MUTE FOREVER
|
|
89
|
+
else if (duration === 'forever' || duration === 'disable') {
|
|
90
|
+
config.set('presenceAgentEnabled', false);
|
|
91
|
+
config.set('mutedUntil', null);
|
|
92
|
+
changes.push('alerts → **disabled**');
|
|
93
|
+
}
|
|
94
|
+
// PARSE DURATION
|
|
95
|
+
else {
|
|
96
|
+
let ms = 0;
|
|
97
|
+
const match = duration.match(/^(\d+)(h|m|min|hour|hours|minutes)$/);
|
|
98
|
+
|
|
99
|
+
if (match) {
|
|
100
|
+
const amount = parseInt(match[1]);
|
|
101
|
+
const unit = match[2];
|
|
102
|
+
|
|
103
|
+
if (unit === 'h' || unit === 'hour' || unit === 'hours') {
|
|
104
|
+
ms = amount * 60 * 60 * 1000;
|
|
105
|
+
} else if (unit === 'm' || unit === 'min' || unit === 'minutes') {
|
|
106
|
+
ms = amount * 60 * 1000;
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
// Default to 1 hour if can't parse
|
|
110
|
+
ms = 60 * 60 * 1000;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const mutedUntil = Date.now() + ms;
|
|
114
|
+
config.set('mutedUntil', mutedUntil);
|
|
115
|
+
|
|
116
|
+
const until = new Date(mutedUntil).toLocaleTimeString();
|
|
117
|
+
const durationText = ms >= 3600000
|
|
118
|
+
? `${Math.floor(ms / 3600000)} hour${Math.floor(ms / 3600000) > 1 ? 's' : ''}`
|
|
119
|
+
: `${Math.floor(ms / 60000)} minutes`;
|
|
120
|
+
|
|
121
|
+
changes.push(`alerts → **muted for ${durationText}** (until ${until})`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Update auto context if provided
|
|
126
|
+
if (args.auto_context !== undefined) {
|
|
127
|
+
config.set('autoContext', args.auto_context);
|
|
128
|
+
changes.push('auto context → **' + (args.auto_context ? 'on' : 'off') + '**');
|
|
129
|
+
}
|
|
130
|
+
|
|
68
131
|
// If no args, show current settings
|
|
69
132
|
if (changes.length === 0) {
|
|
70
133
|
const notifications = config.getNotifications();
|
|
71
134
|
const guided = config.getGuidedMode();
|
|
72
135
|
const githubActivity = config.getGithubActivityEnabled();
|
|
73
136
|
const githubPrivacy = config.getGithubActivityPrivacy();
|
|
137
|
+
const mutedUntil = config.get('mutedUntil');
|
|
138
|
+
const presenceEnabled = config.get('presenceAgentEnabled') !== false;
|
|
139
|
+
const autoContext = config.get('autoContext', true); // Default to true
|
|
74
140
|
|
|
75
141
|
const notifyDesc = {
|
|
76
142
|
all: 'All notifications (messages, mentions, presence)',
|
|
@@ -84,21 +150,43 @@ async function handler(args) {
|
|
|
84
150
|
off: 'Disabled'
|
|
85
151
|
};
|
|
86
152
|
|
|
153
|
+
// Determine mute status
|
|
154
|
+
let muteStatus = 'off';
|
|
155
|
+
let muteDesc = 'Alerts enabled';
|
|
156
|
+
if (!presenceEnabled) {
|
|
157
|
+
muteStatus = 'disabled';
|
|
158
|
+
muteDesc = 'Presence alerts permanently disabled';
|
|
159
|
+
} else if (mutedUntil) {
|
|
160
|
+
const remaining = mutedUntil - Date.now();
|
|
161
|
+
if (remaining > 0) {
|
|
162
|
+
const mins = Math.floor(remaining / 60000);
|
|
163
|
+
const hours = Math.floor(mins / 60);
|
|
164
|
+
muteStatus = hours > 0 ? `${hours}h ${mins % 60}m` : `${mins}m`;
|
|
165
|
+
muteDesc = `Muted until ${new Date(mutedUntil).toLocaleTimeString()}`;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
87
169
|
return {
|
|
88
170
|
display: '## /vibe Settings\n\n' +
|
|
89
171
|
'**Notifications:** ' + notifications + '\n' +
|
|
90
172
|
'_' + notifyDesc[notifications] + '_\n\n' +
|
|
91
173
|
'**Guided Mode:** ' + (guided ? 'on' : 'off') + '\n' +
|
|
92
174
|
'_' + (guided ? 'Shows dashboard menus' : 'Freeform mode') + '_\n\n' +
|
|
175
|
+
'**Alert Mute:** ' + muteStatus + '\n' +
|
|
176
|
+
'_' + muteDesc + '_\n\n' +
|
|
93
177
|
'**GitHub Activity:** ' + (githubActivity ? 'on' : 'off') + '\n' +
|
|
94
178
|
'_' + (githubActivity ? 'Shows shipping status from your GitHub commits' : 'Not sharing GitHub activity') + '_\n\n' +
|
|
95
179
|
(githubActivity ? '**GitHub Privacy:** ' + githubPrivacy + '\n_' + privacyDesc[githubPrivacy] + '_\n\n' : '') +
|
|
180
|
+
'**Auto Context:** ' + (autoContext ? 'on' : 'off') + '\n' +
|
|
181
|
+
'_' + (autoContext ? 'Git branch & work summary shared in presence' : 'Work context not auto-shared') + '_\n\n' +
|
|
96
182
|
'---\n\n' +
|
|
97
183
|
'**Change settings:**\n' +
|
|
98
184
|
'• "set notifications to mentions" — less noisy\n' +
|
|
99
185
|
'• "turn off guided mode" — no menus\n' +
|
|
186
|
+
'• "mute for 2h" — silence alerts temporarily\n' +
|
|
100
187
|
'• "enable github activity" — show when you\'re shipping\n' +
|
|
101
|
-
'• "set github privacy to status_only" — just badge, no details'
|
|
188
|
+
'• "set github privacy to status_only" — just badge, no details\n' +
|
|
189
|
+
'• "disable auto context" — stop sharing git branch/work'
|
|
102
190
|
};
|
|
103
191
|
}
|
|
104
192
|
|