slashvibe-mcp 0.3.20 → 0.3.21
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 +47 -252
- 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 +116 -159
- 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 +21 -16
- package/prompts.js +1 -1
- package/protocol/index.js +73 -0
- package/setup.js +480 -0
- package/smart-inbox.js +276 -0
- package/store/api.js +536 -215
- 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 +325 -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/health.js +87 -0
- package/tools/help.js +3 -3
- package/tools/idea.js +9 -2
- package/tools/inbox.js +289 -105
- package/tools/init.js +131 -34
- package/tools/invite.js +15 -4
- package/tools/leaderboard.js +117 -0
- package/tools/lib/git-apply.js +206 -0
- package/tools/lib/git-bundle.js +407 -0
- 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 +467 -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/streak.js +147 -0
- 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/approve.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_approve — Respond to a plan with approval, rejection, or request changes
|
|
3
|
+
*
|
|
4
|
+
* Only people in the plan's requested_from list can respond.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const config = require('../config');
|
|
8
|
+
const store = require('../store');
|
|
9
|
+
const { requireInit, normalizeHandle } = require('./_shared');
|
|
10
|
+
|
|
11
|
+
const definition = {
|
|
12
|
+
name: 'vibe_approve',
|
|
13
|
+
description: 'Approve, reject, or request changes on a plan. Only invited approvers can respond.',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
plan_id: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Plan ID or slug (e.g., "claude-md-cleanup" or "plan_123...")'
|
|
20
|
+
},
|
|
21
|
+
decision: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
enum: ['approve', 'needs_changes', 'reject'],
|
|
24
|
+
description: 'Your decision: approve, needs_changes, or reject'
|
|
25
|
+
},
|
|
26
|
+
note: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Optional feedback or reason for your decision'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
required: ['plan_id', 'decision']
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async function handler(args) {
|
|
36
|
+
const initCheck = requireInit();
|
|
37
|
+
if (initCheck) return initCheck;
|
|
38
|
+
|
|
39
|
+
const { plan_id, decision, note } = args;
|
|
40
|
+
const myHandle = config.getHandle();
|
|
41
|
+
|
|
42
|
+
// Fetch the plan
|
|
43
|
+
const planResult = await store.getArtifact(plan_id);
|
|
44
|
+
|
|
45
|
+
if (!planResult.success || !planResult.artifact) {
|
|
46
|
+
return {
|
|
47
|
+
display: `❌ Plan not found: ${plan_id}\n\nTip: Use the plan slug (e.g., "claude-md-cleanup") or full ID.`
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const plan = planResult.artifact;
|
|
52
|
+
|
|
53
|
+
// Verify it's a plan template
|
|
54
|
+
if (plan.template !== 'plan') {
|
|
55
|
+
return {
|
|
56
|
+
display: `❌ "${plan_id}" is not a plan (it's a ${plan.template})`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Verify it has approval workflow
|
|
61
|
+
if (!plan.approval) {
|
|
62
|
+
return {
|
|
63
|
+
display: `❌ This plan doesn't have an approval workflow`
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check if user is in the requested_from list
|
|
68
|
+
if (!plan.approval.requested_from.includes(myHandle)) {
|
|
69
|
+
return {
|
|
70
|
+
display: `❌ You're not on the approver list for this plan.\n\n**Approvers:** ${plan.approval.requested_from.map(h => `@${h}`).join(', ')}`
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Check if plan is still pending
|
|
75
|
+
if (plan.approval.status !== 'pending') {
|
|
76
|
+
return {
|
|
77
|
+
display: `⚠️ This plan is already ${plan.approval.status}.\n\nNo changes made.`
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check if user already responded
|
|
82
|
+
const existingResponse = plan.approval.responses.find(r => r.handle === myHandle);
|
|
83
|
+
if (existingResponse) {
|
|
84
|
+
return {
|
|
85
|
+
display: `⚠️ You already responded to this plan (${existingResponse.status}).\n\nTo change your response, the plan creator would need to request again.`
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Record the response
|
|
90
|
+
const response = {
|
|
91
|
+
handle: myHandle,
|
|
92
|
+
status: decision,
|
|
93
|
+
note: note || null,
|
|
94
|
+
responded_at: new Date().toISOString()
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
plan.approval.responses.push(response);
|
|
98
|
+
plan.updated_at = new Date().toISOString();
|
|
99
|
+
|
|
100
|
+
// Determine new status based on decision and requires mode
|
|
101
|
+
let newStatus = 'pending';
|
|
102
|
+
|
|
103
|
+
if (decision === 'approve') {
|
|
104
|
+
if (plan.approval.requires === 'any') {
|
|
105
|
+
// Any approval is enough
|
|
106
|
+
newStatus = 'approved';
|
|
107
|
+
} else {
|
|
108
|
+
// All must approve - check if everyone has approved
|
|
109
|
+
const allApproved = plan.approval.requested_from.every(h =>
|
|
110
|
+
plan.approval.responses.some(r => r.handle === h && r.status === 'approve')
|
|
111
|
+
);
|
|
112
|
+
newStatus = allApproved ? 'approved' : 'pending';
|
|
113
|
+
}
|
|
114
|
+
} else if (decision === 'reject') {
|
|
115
|
+
// Any rejection rejects the plan
|
|
116
|
+
newStatus = 'rejected';
|
|
117
|
+
} else if (decision === 'needs_changes') {
|
|
118
|
+
// Any needs_changes puts it in that state
|
|
119
|
+
newStatus = 'needs_changes';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
plan.approval.status = newStatus;
|
|
123
|
+
if (newStatus !== 'pending') {
|
|
124
|
+
plan.approval.resolved_at = new Date().toISOString();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Save the updated plan
|
|
128
|
+
const updateResult = await store.updateArtifact(plan.id, plan);
|
|
129
|
+
|
|
130
|
+
if (!updateResult.success) {
|
|
131
|
+
return {
|
|
132
|
+
display: `❌ Failed to save response: ${updateResult.error}`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Notify the plan creator
|
|
137
|
+
const creator = plan.created_by;
|
|
138
|
+
const statusEmoji = {
|
|
139
|
+
approve: '✅',
|
|
140
|
+
needs_changes: '🔄',
|
|
141
|
+
reject: '❌'
|
|
142
|
+
}[decision];
|
|
143
|
+
|
|
144
|
+
const statusText = {
|
|
145
|
+
approve: 'approved',
|
|
146
|
+
needs_changes: 'requested changes on',
|
|
147
|
+
reject: 'rejected'
|
|
148
|
+
}[decision];
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
await store.sendMessage(myHandle, creator,
|
|
152
|
+
`${statusEmoji} **@${myHandle} ${statusText} your plan**\n\n` +
|
|
153
|
+
`**Plan:** ${plan.title}\n` +
|
|
154
|
+
(note ? `**Note:** ${note}\n` : '') +
|
|
155
|
+
`**Status:** ${newStatus === 'pending' ? '⏳ Still pending (awaiting more approvers)' : `${statusEmoji} ${newStatus.charAt(0).toUpperCase() + newStatus.slice(1)}`}`,
|
|
156
|
+
{
|
|
157
|
+
type: 'plan_response',
|
|
158
|
+
plan_id: plan.id,
|
|
159
|
+
plan_slug: plan.slug,
|
|
160
|
+
decision,
|
|
161
|
+
new_status: newStatus
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error('[APPROVE] Failed to notify creator:', error);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Build response
|
|
169
|
+
let display = `${statusEmoji} **Response recorded: ${decision}**\n\n`;
|
|
170
|
+
display += `**Plan:** ${plan.title}\n`;
|
|
171
|
+
display += `**Your decision:** ${decision}${note ? ` — "${note}"` : ''}\n`;
|
|
172
|
+
display += `**Plan status:** ${newStatus}\n\n`;
|
|
173
|
+
|
|
174
|
+
if (newStatus === 'approved') {
|
|
175
|
+
display += `✓ Plan is now approved! @${creator} has been notified.`;
|
|
176
|
+
} else if (newStatus === 'rejected') {
|
|
177
|
+
display += `Plan has been rejected. @${creator} has been notified.`;
|
|
178
|
+
} else if (newStatus === 'needs_changes') {
|
|
179
|
+
display += `Plan needs changes. @${creator} has been notified to update.`;
|
|
180
|
+
} else {
|
|
181
|
+
const remaining = plan.approval.requested_from.filter(h =>
|
|
182
|
+
!plan.approval.responses.some(r => r.handle === h)
|
|
183
|
+
);
|
|
184
|
+
display += `⏳ Waiting for: ${remaining.map(h => `@${h}`).join(', ')}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
display,
|
|
189
|
+
plan_id: plan.id,
|
|
190
|
+
plan_slug: plan.slug,
|
|
191
|
+
decision,
|
|
192
|
+
new_status: newStatus,
|
|
193
|
+
creator_notified: true
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = { definition, handler };
|
package/tools/artifact-create.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* vibe_create_artifact - Create social artifacts from conversations
|
|
3
3
|
*
|
|
4
4
|
* Artifacts are first-class objects with provenance, permissions, and social context.
|
|
5
|
-
* They can be guides, learnings, or
|
|
5
|
+
* They can be guides, learnings, workspaces, or plans - all shareable via DM.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const config = require('../config');
|
|
@@ -11,7 +11,7 @@ const { requireInit } = require('./_shared');
|
|
|
11
11
|
|
|
12
12
|
const definition = {
|
|
13
13
|
name: 'vibe_create_artifact',
|
|
14
|
-
description: 'Create a social artifact (guide, learning, workspace) from conversation or memory. Artifacts are first-class objects with provenance and can be shared via DM.',
|
|
14
|
+
description: 'Create a social artifact (guide, learning, workspace, plan) from conversation or memory. Artifacts are first-class objects with provenance and can be shared via DM.',
|
|
15
15
|
inputSchema: {
|
|
16
16
|
type: 'object',
|
|
17
17
|
properties: {
|
|
@@ -25,7 +25,7 @@ const definition = {
|
|
|
25
25
|
},
|
|
26
26
|
template: {
|
|
27
27
|
type: 'string',
|
|
28
|
-
enum: ['guide', 'learning', 'workspace'],
|
|
28
|
+
enum: ['guide', 'learning', 'workspace', 'plan'],
|
|
29
29
|
description: 'Structural template to use'
|
|
30
30
|
},
|
|
31
31
|
content: {
|
|
@@ -176,6 +176,17 @@ async function handler(args) {
|
|
|
176
176
|
forked_from: null
|
|
177
177
|
};
|
|
178
178
|
|
|
179
|
+
// Add approval workflow for plan templates
|
|
180
|
+
if (template === 'plan') {
|
|
181
|
+
artifact.approval = {
|
|
182
|
+
status: 'pending',
|
|
183
|
+
requested_from: recipient ? [recipient.replace('@', '')] : [],
|
|
184
|
+
requires: 'any', // 'any' or 'all'
|
|
185
|
+
responses: [],
|
|
186
|
+
resolved_at: null
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
179
190
|
// Store artifact
|
|
180
191
|
const storeResult = await store.createArtifact(artifact);
|
|
181
192
|
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_artifacts_price - Set pricing for an artifact
|
|
3
|
+
*
|
|
4
|
+
* Enables artifact commerce by setting a price.
|
|
5
|
+
* Supports one-time purchase, pay-what-you-want, and tip jar models.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const config = require('../config');
|
|
9
|
+
const { requireInit, success, error, header } = require('./_shared');
|
|
10
|
+
|
|
11
|
+
const BASE_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
12
|
+
|
|
13
|
+
const definition = {
|
|
14
|
+
name: 'vibe_artifacts_price',
|
|
15
|
+
description: 'Set pricing for an artifact you created. Enable commerce for your artifacts.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
artifact_id: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'The artifact ID to price (e.g., art_abc123)'
|
|
22
|
+
},
|
|
23
|
+
price: {
|
|
24
|
+
type: 'number',
|
|
25
|
+
description: 'Price in dollars (e.g., 5.00). Set to 0 to make free.'
|
|
26
|
+
},
|
|
27
|
+
sale_type: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
enum: ['one_time', 'pay_what_you_want', 'tip'],
|
|
30
|
+
description: 'Sale type: one_time (fixed price), pay_what_you_want (price is minimum), tip (free with optional tip)'
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
required: ['artifact_id', 'price', 'sale_type']
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
async function handler(args) {
|
|
38
|
+
const initCheck = requireInit();
|
|
39
|
+
if (initCheck) return initCheck;
|
|
40
|
+
|
|
41
|
+
const { artifact_id, price, sale_type } = args;
|
|
42
|
+
const handle = config.getHandle();
|
|
43
|
+
|
|
44
|
+
// Validate price
|
|
45
|
+
if (price < 0) {
|
|
46
|
+
return { display: error('Price cannot be negative') };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (price > 1000) {
|
|
50
|
+
return { display: error('Price cannot exceed $1000') };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(`${BASE_URL}/api/artifacts/price`, {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
artifact_id,
|
|
59
|
+
handle,
|
|
60
|
+
price_cents: Math.round(price * 100),
|
|
61
|
+
sale_type
|
|
62
|
+
})
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
|
|
67
|
+
if (!data.success) {
|
|
68
|
+
return { display: error(data.error || 'Failed to set pricing') };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Format display
|
|
72
|
+
let display = success('Artifact Pricing Updated') + '\n\n';
|
|
73
|
+
|
|
74
|
+
display += header('Pricing Details', 3) + '\n';
|
|
75
|
+
display += `**Artifact:** ${data.artifact?.title || artifact_id}\n`;
|
|
76
|
+
display += `**Price:** $${price.toFixed(2)}\n`;
|
|
77
|
+
display += `**Sale Type:** ${formatSaleType(sale_type)}\n`;
|
|
78
|
+
display += `**Status:** ${price > 0 ? 'For Sale' : 'Free'}\n\n`;
|
|
79
|
+
|
|
80
|
+
if (data.artifact?.url) {
|
|
81
|
+
display += `**Share Link:** ${data.artifact.url}\n`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (sale_type === 'pay_what_you_want') {
|
|
85
|
+
display += `\n_Buyers can pay $${price.toFixed(2)} or more_`;
|
|
86
|
+
} else if (sale_type === 'tip') {
|
|
87
|
+
display += `\n_Free to access, tips optional_`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { display };
|
|
91
|
+
|
|
92
|
+
} catch (e) {
|
|
93
|
+
console.error('[artifacts-price] Error:', e);
|
|
94
|
+
return { display: error(`Failed to set pricing: ${e.message}`) };
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function formatSaleType(type) {
|
|
99
|
+
const types = {
|
|
100
|
+
one_time: 'One-Time Purchase',
|
|
101
|
+
pay_what_you_want: 'Pay What You Want',
|
|
102
|
+
tip: 'Free with Tips'
|
|
103
|
+
};
|
|
104
|
+
return types[type] || type;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { definition, handler };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe available — Signal you're open to chat
|
|
3
|
+
*
|
|
4
|
+
* Lower the barrier to connection by explicitly signaling
|
|
5
|
+
* what topics you're happy to help with.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* - vibe available "React, auth, Postgres"
|
|
9
|
+
* - vibe available --clear
|
|
10
|
+
* - vibe available (show current)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const config = require('../config');
|
|
14
|
+
const store = require('../store');
|
|
15
|
+
const { requireInit } = require('./_shared');
|
|
16
|
+
|
|
17
|
+
const definition = {
|
|
18
|
+
name: 'vibe_available',
|
|
19
|
+
description: 'Signal you\'re open to chat about specific topics. Others see you\'re available and what you can help with.',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
topics: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Topics you\'re available to help with (e.g., "React, auth, Postgres")'
|
|
26
|
+
},
|
|
27
|
+
clear: {
|
|
28
|
+
type: 'boolean',
|
|
29
|
+
description: 'Clear your availability - stop signaling you\'re open'
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
async function handler(args) {
|
|
36
|
+
const initCheck = requireInit();
|
|
37
|
+
if (initCheck) return initCheck;
|
|
38
|
+
|
|
39
|
+
const myHandle = config.getHandle();
|
|
40
|
+
const { topics, clear } = args;
|
|
41
|
+
|
|
42
|
+
// Clear availability
|
|
43
|
+
if (clear) {
|
|
44
|
+
await store.heartbeat(myHandle, config.getOneLiner(), {
|
|
45
|
+
availableFor: null
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
display: `## Availability Cleared
|
|
50
|
+
|
|
51
|
+
You're no longer signaling openness to chat.
|
|
52
|
+
|
|
53
|
+
Others won't see availability topics next to your name.
|
|
54
|
+
|
|
55
|
+
**To signal availability again:**
|
|
56
|
+
\`vibe available "React, auth, Postgres"\``
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Show current availability
|
|
61
|
+
if (!topics) {
|
|
62
|
+
return {
|
|
63
|
+
display: `## Signal Availability
|
|
64
|
+
|
|
65
|
+
Let others know you're open to chat about specific topics.
|
|
66
|
+
|
|
67
|
+
**Set your availability:**
|
|
68
|
+
\`vibe available "React, auth, Postgres"\`
|
|
69
|
+
|
|
70
|
+
**What happens:**
|
|
71
|
+
- Your presence shows: \`@${myHandle} — available for: React, auth, Postgres\`
|
|
72
|
+
- Others know they won't be bothering you
|
|
73
|
+
- Lower barrier to reaching out
|
|
74
|
+
|
|
75
|
+
**Clear availability:**
|
|
76
|
+
\`vibe available --clear\`
|
|
77
|
+
|
|
78
|
+
_Availability is ephemeral — clears when you go offline._`
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Parse topics (comma or space separated)
|
|
83
|
+
const topicList = topics
|
|
84
|
+
.split(/[,\s]+/)
|
|
85
|
+
.map(t => t.trim().toLowerCase())
|
|
86
|
+
.filter(t => t.length > 0 && t.length <= 30)
|
|
87
|
+
.slice(0, 5); // Max 5 topics
|
|
88
|
+
|
|
89
|
+
if (topicList.length === 0) {
|
|
90
|
+
return {
|
|
91
|
+
display: `Please provide at least one topic. Example:\n\`vibe available "React, auth, Postgres"\``
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Update presence with availability
|
|
96
|
+
await store.heartbeat(myHandle, config.getOneLiner(), {
|
|
97
|
+
availableFor: topicList
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const topicsFormatted = topicList.join(', ');
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
display: `## You're Available! 🤝
|
|
104
|
+
|
|
105
|
+
**Topics:** ${topicsFormatted}
|
|
106
|
+
|
|
107
|
+
Others now see:
|
|
108
|
+
\`@${myHandle} — available for: ${topicsFormatted}\`
|
|
109
|
+
|
|
110
|
+
This signals you're open to questions about these topics.
|
|
111
|
+
People are more likely to reach out when they know they won't be bothering you.
|
|
112
|
+
|
|
113
|
+
**Clear when busy:**
|
|
114
|
+
\`vibe available --clear\`
|
|
115
|
+
|
|
116
|
+
_Availability clears automatically when you go offline._`
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = { definition, handler };
|