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/plan.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_plan — Create a plan and request approval
|
|
3
|
+
*
|
|
4
|
+
* Plans are structured proposals that can be sent for async approval.
|
|
5
|
+
* They're artifacts with an approval workflow built in.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const config = require('../config');
|
|
9
|
+
const store = require('../store');
|
|
10
|
+
const { requireInit, normalizeHandle } = require('./_shared');
|
|
11
|
+
|
|
12
|
+
const definition = {
|
|
13
|
+
name: 'vibe_plan',
|
|
14
|
+
description: 'Create a plan and request approval from collaborators. Plans are structured proposals with summary, problem, changes, and impact.',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
title: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Plan title (e.g., "Refactor auth system", "CLAUDE.md cleanup")'
|
|
21
|
+
},
|
|
22
|
+
summary: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'One-line summary of what this plan does'
|
|
25
|
+
},
|
|
26
|
+
problem: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'What problem does this solve? (optional)'
|
|
29
|
+
},
|
|
30
|
+
changes: {
|
|
31
|
+
type: 'array',
|
|
32
|
+
items: { type: 'string' },
|
|
33
|
+
description: 'List of proposed changes'
|
|
34
|
+
},
|
|
35
|
+
impact: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'What will be affected by this change? (optional)'
|
|
38
|
+
},
|
|
39
|
+
request_approval_from: {
|
|
40
|
+
type: 'array',
|
|
41
|
+
items: { type: 'string' },
|
|
42
|
+
description: 'Handles to request approval from (e.g., ["@brightseth", "@alice"])'
|
|
43
|
+
},
|
|
44
|
+
requires: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
enum: ['any', 'all'],
|
|
47
|
+
description: 'Require approval from any one person (default) or all of them'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
required: ['title', 'summary', 'changes', 'request_approval_from']
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Generate artifact ID and slug
|
|
55
|
+
function generateArtifactId() {
|
|
56
|
+
return `plan_${Date.now()}_${Math.random().toString(36).substring(7)}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function generateSlug(title) {
|
|
60
|
+
return title
|
|
61
|
+
.toLowerCase()
|
|
62
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
63
|
+
.replace(/^-|-$/g, '')
|
|
64
|
+
.substring(0, 60);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function handler(args) {
|
|
68
|
+
const initCheck = requireInit();
|
|
69
|
+
if (initCheck) return initCheck;
|
|
70
|
+
|
|
71
|
+
const {
|
|
72
|
+
title,
|
|
73
|
+
summary,
|
|
74
|
+
problem,
|
|
75
|
+
changes,
|
|
76
|
+
impact,
|
|
77
|
+
request_approval_from,
|
|
78
|
+
requires = 'any'
|
|
79
|
+
} = args;
|
|
80
|
+
|
|
81
|
+
const creator = config.getHandle();
|
|
82
|
+
|
|
83
|
+
// Validate approvers
|
|
84
|
+
if (!request_approval_from || request_approval_from.length === 0) {
|
|
85
|
+
return {
|
|
86
|
+
display: '❌ Must specify at least one approver in request_approval_from'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Normalize approver handles
|
|
91
|
+
const approvers = request_approval_from.map(h => normalizeHandle(h));
|
|
92
|
+
|
|
93
|
+
// Build content blocks for the plan
|
|
94
|
+
const blocks = [
|
|
95
|
+
{ type: 'summary', data: { text: summary } }
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
if (problem) {
|
|
99
|
+
blocks.push({ type: 'problem', data: { text: problem } });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
blocks.push({
|
|
103
|
+
type: 'proposed_changes',
|
|
104
|
+
data: { items: changes }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (impact) {
|
|
108
|
+
blocks.push({ type: 'impact', data: { text: impact } });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Build the plan artifact
|
|
112
|
+
const planId = generateArtifactId();
|
|
113
|
+
const slug = generateSlug(title);
|
|
114
|
+
|
|
115
|
+
// Build audience list (creator + all approvers)
|
|
116
|
+
const audience = new Set([creator, ...approvers]);
|
|
117
|
+
|
|
118
|
+
const plan = {
|
|
119
|
+
id: planId,
|
|
120
|
+
slug,
|
|
121
|
+
title,
|
|
122
|
+
template: 'plan',
|
|
123
|
+
content: { blocks },
|
|
124
|
+
|
|
125
|
+
// Social metadata
|
|
126
|
+
created_by: creator,
|
|
127
|
+
created_for: approvers[0], // Primary approver
|
|
128
|
+
thread_id: null,
|
|
129
|
+
|
|
130
|
+
// Privacy - plans are unlisted by default
|
|
131
|
+
visibility: 'unlisted',
|
|
132
|
+
audience: Array.from(audience),
|
|
133
|
+
|
|
134
|
+
// Provenance
|
|
135
|
+
provenance: {
|
|
136
|
+
source_type: 'manual',
|
|
137
|
+
personalized_for: null,
|
|
138
|
+
notes: null
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
// Lifecycle
|
|
142
|
+
created_at: new Date().toISOString(),
|
|
143
|
+
updated_at: new Date().toISOString(),
|
|
144
|
+
expires_at: null,
|
|
145
|
+
|
|
146
|
+
// Evolution
|
|
147
|
+
revision: 1,
|
|
148
|
+
forked_from: null,
|
|
149
|
+
|
|
150
|
+
// Approval workflow
|
|
151
|
+
approval: {
|
|
152
|
+
status: 'pending',
|
|
153
|
+
requested_from: approvers,
|
|
154
|
+
requires,
|
|
155
|
+
responses: [],
|
|
156
|
+
resolved_at: null
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Store the plan
|
|
161
|
+
const storeResult = await store.createArtifact(plan);
|
|
162
|
+
|
|
163
|
+
if (!storeResult.success) {
|
|
164
|
+
return {
|
|
165
|
+
display: `❌ Failed to create plan: ${storeResult.error}`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const planUrl = `https://slashvibe.dev/a/${slug}`;
|
|
170
|
+
|
|
171
|
+
// Send DM to each approver
|
|
172
|
+
const dmResults = [];
|
|
173
|
+
for (const approver of approvers) {
|
|
174
|
+
try {
|
|
175
|
+
const dmResult = await store.sendMessage(creator, approver,
|
|
176
|
+
`📋 **Plan: ${title}**\n\n` +
|
|
177
|
+
`**Summary:** ${summary}\n\n` +
|
|
178
|
+
(problem ? `**Problem:** ${problem}\n\n` : '') +
|
|
179
|
+
`**Changes:**\n${changes.map(c => `• ${c}`).join('\n')}\n\n` +
|
|
180
|
+
(impact ? `**Impact:** ${impact}\n\n` : '') +
|
|
181
|
+
`---\n` +
|
|
182
|
+
`👍 Approve · 🔄 Request changes · ❌ Reject\n` +
|
|
183
|
+
`Use: \`vibe approve ${slug}\` or \`vibe approve ${slug} --reject\``,
|
|
184
|
+
{
|
|
185
|
+
type: 'plan_card',
|
|
186
|
+
plan_id: planId,
|
|
187
|
+
plan_slug: slug,
|
|
188
|
+
plan_url: planUrl
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
dmResults.push({ approver, success: dmResult.success });
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error(`[PLAN] Failed to DM ${approver}:`, error);
|
|
194
|
+
dmResults.push({ approver, success: false, error: error.message });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Build response
|
|
199
|
+
const successfulDMs = dmResults.filter(r => r.success).length;
|
|
200
|
+
const approverList = approvers.map(a => `@${a}`).join(', ');
|
|
201
|
+
|
|
202
|
+
let display = `📋 **Plan created: ${title}**\n\n`;
|
|
203
|
+
display += `**Status:** ⏳ Pending approval\n`;
|
|
204
|
+
display += `**Requested from:** ${approverList}\n`;
|
|
205
|
+
display += `**Requires:** ${requires === 'all' ? 'All approvers' : 'Any approver'}\n\n`;
|
|
206
|
+
|
|
207
|
+
if (successfulDMs === approvers.length) {
|
|
208
|
+
display += `✓ Sent to ${successfulDMs} approver${successfulDMs > 1 ? 's' : ''}\n`;
|
|
209
|
+
} else {
|
|
210
|
+
display += `⚠️ Sent to ${successfulDMs}/${approvers.length} approvers\n`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
display += `\n**Plan ID:** \`${slug}\``;
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
display,
|
|
217
|
+
plan_id: planId,
|
|
218
|
+
plan_slug: slug,
|
|
219
|
+
plan_url: planUrl,
|
|
220
|
+
approval_status: 'pending',
|
|
221
|
+
approvers
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module.exports = { definition, handler };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_proof_of_work - Get unified proof-of-work summary for a user
|
|
3
|
+
*
|
|
4
|
+
* Shows the complete "proof of work" picture:
|
|
5
|
+
* - PAST: Artifacts created/sold, sessions shared
|
|
6
|
+
* - PRESENT: Live presence, current work
|
|
7
|
+
* - FUTURE: Gigs completed/posted, hire availability
|
|
8
|
+
* - UNIFIED: Vibe score, tier, proven skills
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const config = require('../config');
|
|
12
|
+
const { requireInit, normalizeHandle, displayHandle, header, divider, emptyState, formatTimeAgo } = require('./_shared');
|
|
13
|
+
const { actions, formatActions } = require('./_actions');
|
|
14
|
+
|
|
15
|
+
const BASE_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
|
|
16
|
+
|
|
17
|
+
const definition = {
|
|
18
|
+
name: 'vibe_proof_of_work',
|
|
19
|
+
description: 'Get proof-of-work summary for a user. Shows artifacts, sessions, gigs, vibe score, and proven skills.',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
handle: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'User handle to look up (default: yourself)'
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
async function handler(args) {
|
|
32
|
+
const initCheck = requireInit();
|
|
33
|
+
if (initCheck) return initCheck;
|
|
34
|
+
|
|
35
|
+
const targetHandle = normalizeHandle(args.handle) || config.getHandle();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(`${BASE_URL}/api/proof-of-work?handle=${encodeURIComponent(targetHandle)}`);
|
|
39
|
+
const data = await response.json();
|
|
40
|
+
|
|
41
|
+
if (!data.success) {
|
|
42
|
+
return { display: `❌ ${data.error || 'Failed to fetch proof-of-work'}` };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const pow = data.proof_of_work;
|
|
46
|
+
const isSelf = targetHandle === config.getHandle();
|
|
47
|
+
|
|
48
|
+
// Build display
|
|
49
|
+
let display = header(`Proof of Work: ${displayHandle(targetHandle)}`, 2) + '\n\n';
|
|
50
|
+
|
|
51
|
+
// Vibe Score & Tier
|
|
52
|
+
display += `**Vibe Score:** ${pow.unified?.vibe_score || 0}\n`;
|
|
53
|
+
display += `**Tier:** ${formatTier(pow.unified?.tier)}\n\n`;
|
|
54
|
+
|
|
55
|
+
// ━━━ PAST ━━━
|
|
56
|
+
display += header('📜 PAST (Artifacts & Sessions)', 3) + '\n';
|
|
57
|
+
const past = pow.past || {};
|
|
58
|
+
display += `• **Artifacts Created:** ${past.artifacts_created || 0}\n`;
|
|
59
|
+
display += `• **Artifacts Sold:** ${past.artifacts_sold || 0}\n`;
|
|
60
|
+
display += `• **Revenue Earned:** $${((past.revenue_cents || 0) / 100).toFixed(2)}\n`;
|
|
61
|
+
display += `• **Sessions Shared:** ${past.sessions_shared || 0}\n\n`;
|
|
62
|
+
|
|
63
|
+
// ━━━ PRESENT ━━━
|
|
64
|
+
display += header('🟢 PRESENT (Live)', 3) + '\n';
|
|
65
|
+
const present = pow.present || {};
|
|
66
|
+
if (present.is_online) {
|
|
67
|
+
display += `• **Status:** Online\n`;
|
|
68
|
+
if (present.working_on) {
|
|
69
|
+
display += `• **Working on:** ${present.working_on}\n`;
|
|
70
|
+
}
|
|
71
|
+
if (present.project) {
|
|
72
|
+
display += `• **Project:** ${present.project}\n`;
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
display += `• **Status:** Offline\n`;
|
|
76
|
+
if (present.last_seen) {
|
|
77
|
+
display += `• **Last seen:** ${formatTimeAgo(present.last_seen)}\n`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
display += '\n';
|
|
81
|
+
|
|
82
|
+
// ━━━ FUTURE ━━━
|
|
83
|
+
display += header('🚀 FUTURE (Gigs & Availability)', 3) + '\n';
|
|
84
|
+
const future = pow.future || {};
|
|
85
|
+
display += `• **Gigs Completed:** ${future.gigs_completed || 0}\n`;
|
|
86
|
+
display += `• **Gigs Posted:** ${future.gigs_posted || 0}\n`;
|
|
87
|
+
display += `• **Avg Rating:** ${future.avg_rating ? `${future.avg_rating.toFixed(1)}⭐` : 'No ratings yet'}\n`;
|
|
88
|
+
display += `• **Available for Hire:** ${future.available_for_hire ? 'Yes' : 'No'}\n\n`;
|
|
89
|
+
|
|
90
|
+
// ━━━ PROVEN SKILLS ━━━
|
|
91
|
+
const skills = pow.unified?.proven_skills || [];
|
|
92
|
+
if (skills.length > 0) {
|
|
93
|
+
display += header('🛠️ Proven Skills', 3) + '\n';
|
|
94
|
+
skills.slice(0, 10).forEach(skill => {
|
|
95
|
+
display += `• ${skill.name} (${skill.count} gig${skill.count !== 1 ? 's' : ''})\n`;
|
|
96
|
+
});
|
|
97
|
+
display += '\n';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ━━━ SCORE BREAKDOWN ━━━
|
|
101
|
+
const breakdown = pow.unified?.score_breakdown;
|
|
102
|
+
if (breakdown) {
|
|
103
|
+
display += header('📊 Score Breakdown', 3) + '\n';
|
|
104
|
+
display += `• Gigs: ${breakdown.gigs || 0}\n`;
|
|
105
|
+
display += `• Artifacts: ${breakdown.artifacts || 0}\n`;
|
|
106
|
+
display += `• Sessions: ${breakdown.sessions || 0}\n`;
|
|
107
|
+
display += `• Invites: ${breakdown.invites || 0}\n`;
|
|
108
|
+
display += `• Ships: ${breakdown.ships || 0}\n`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// CTA for self
|
|
112
|
+
if (isSelf) {
|
|
113
|
+
display += divider();
|
|
114
|
+
display += '💡 **Boost your score:** Complete gigs (+50), sell artifacts (+30), share sessions (+10)\n';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Build result with tip actions
|
|
118
|
+
const result = { display };
|
|
119
|
+
result.actions = formatActions(actions.afterProofOfWork(targetHandle, isSelf));
|
|
120
|
+
|
|
121
|
+
return result;
|
|
122
|
+
|
|
123
|
+
} catch (e) {
|
|
124
|
+
console.error('[proof-of-work] Error:', e);
|
|
125
|
+
return { display: `❌ Failed to fetch proof-of-work: ${e.message}` };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Format tier with emoji
|
|
131
|
+
*/
|
|
132
|
+
function formatTier(tier) {
|
|
133
|
+
const tiers = {
|
|
134
|
+
newcomer: '🌱 Newcomer',
|
|
135
|
+
rising: '📈 Rising',
|
|
136
|
+
proven: '✅ Proven',
|
|
137
|
+
established: '🏆 Established',
|
|
138
|
+
expert: '⭐ Expert',
|
|
139
|
+
legendary: '👑 Legendary'
|
|
140
|
+
};
|
|
141
|
+
return tiers[tier] || tier || 'Unknown';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = { definition, handler };
|
package/tools/reply.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe reply — Quick reply to the most recent unread message
|
|
3
|
+
*
|
|
4
|
+
* Streamlined flow: one command instead of inbox → open → dm
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const config = require('../config');
|
|
8
|
+
const store = require('../store');
|
|
9
|
+
const patterns = require('../intelligence/patterns');
|
|
10
|
+
const userProfiles = require('../store/profiles');
|
|
11
|
+
const { trackMessage } = require('./summarize');
|
|
12
|
+
const { requireInit, normalizeHandle, truncate, warning, fetchRelevantUsers } = require('./_shared');
|
|
13
|
+
const { actions, formatActions } = require('./_actions');
|
|
14
|
+
|
|
15
|
+
const definition = {
|
|
16
|
+
name: 'vibe_reply',
|
|
17
|
+
description: 'Quick reply to your most recent unread message, or to a specific person. Streamlined: one command instead of inbox → open → dm.',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {
|
|
21
|
+
message: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Your reply message'
|
|
24
|
+
},
|
|
25
|
+
to: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Optional: reply to a specific person (e.g., @alex). If not provided, replies to most recent unread.'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
required: ['message']
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
async function handler(args) {
|
|
35
|
+
const initCheck = requireInit();
|
|
36
|
+
if (initCheck) return initCheck;
|
|
37
|
+
|
|
38
|
+
const { message, to } = args;
|
|
39
|
+
const myHandle = config.getHandle();
|
|
40
|
+
|
|
41
|
+
if (!message || message.trim().length === 0) {
|
|
42
|
+
return { display: 'Need a message to reply with.' };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let targetHandle;
|
|
46
|
+
let threadContext = null;
|
|
47
|
+
|
|
48
|
+
// If specific recipient provided, use that
|
|
49
|
+
if (to) {
|
|
50
|
+
targetHandle = normalizeHandle(to);
|
|
51
|
+
} else {
|
|
52
|
+
// Find most recent unread thread
|
|
53
|
+
const threads = await store.getInbox(myHandle);
|
|
54
|
+
|
|
55
|
+
if (!threads || threads.length === 0) {
|
|
56
|
+
return {
|
|
57
|
+
display: '📭 No messages to reply to.\n\nUse `vibe dm @someone "message"` to start a conversation.',
|
|
58
|
+
actions: formatActions(actions.recommendedConnections([]))
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Find first thread with unread messages
|
|
63
|
+
const unreadThread = threads.find(t => t.unread > 0);
|
|
64
|
+
|
|
65
|
+
if (!unreadThread) {
|
|
66
|
+
// No unread, show most recent thread
|
|
67
|
+
const mostRecent = threads[0];
|
|
68
|
+
return {
|
|
69
|
+
display: `📭 All caught up! Most recent: @${mostRecent.handle}\n\nUse \`vibe reply "message" --to @${mostRecent.handle}\` to continue that conversation.`,
|
|
70
|
+
actions: formatActions(actions.afterInboxCompact([{ handle: mostRecent.handle, unread: 0 }]))
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
targetHandle = unreadThread.handle;
|
|
75
|
+
threadContext = {
|
|
76
|
+
unreadCount: unreadThread.unread,
|
|
77
|
+
preview: unreadThread.lastMessage ? truncate(unreadThread.lastMessage, 50) : null
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Can't reply to yourself
|
|
82
|
+
if (targetHandle === myHandle) {
|
|
83
|
+
return { display: 'You can\'t DM yourself.' };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Route @echo messages to the echo agent
|
|
87
|
+
if (targetHandle === 'echo') {
|
|
88
|
+
const echo = require('./echo');
|
|
89
|
+
return echo.handler({ message, anonymous: false });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Truncate message if too long
|
|
93
|
+
const MAX_LENGTH = 2000;
|
|
94
|
+
const trimmed = message.trim();
|
|
95
|
+
const wasTruncated = trimmed.length > MAX_LENGTH;
|
|
96
|
+
const finalMessage = wasTruncated ? trimmed.substring(0, MAX_LENGTH) : trimmed;
|
|
97
|
+
|
|
98
|
+
// Send typing indicator (shows "typing..." to recipient)
|
|
99
|
+
store.sendTypingIndicator(myHandle, targetHandle).catch(() => {});
|
|
100
|
+
|
|
101
|
+
// Send the message
|
|
102
|
+
const result = await store.sendMessage(myHandle, targetHandle, finalMessage, 'dm', null);
|
|
103
|
+
|
|
104
|
+
// Check for errors
|
|
105
|
+
if (result && result.error) {
|
|
106
|
+
return {
|
|
107
|
+
display: `❌ **Failed to send reply**\n\n${result.message}\n\n_Please try again._`
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Mark the thread as read since we're replying
|
|
112
|
+
try {
|
|
113
|
+
await store.markThreadRead(myHandle, targetHandle);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
// Non-fatal - continue
|
|
116
|
+
console.warn('[reply] Failed to mark thread as read:', e.message);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Log social pattern
|
|
120
|
+
patterns.logMessageSent(targetHandle);
|
|
121
|
+
|
|
122
|
+
// Record connection if first time
|
|
123
|
+
try {
|
|
124
|
+
const hasConnected = await userProfiles.hasBeenConnected(myHandle, targetHandle);
|
|
125
|
+
if (!hasConnected) {
|
|
126
|
+
await userProfiles.recordConnection(myHandle, targetHandle, 'first_message');
|
|
127
|
+
}
|
|
128
|
+
} catch (e) {
|
|
129
|
+
console.warn('[reply] Failed to update profile connection:', e);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Track for session summary
|
|
133
|
+
trackMessage(myHandle, targetHandle, 'sent');
|
|
134
|
+
|
|
135
|
+
// Build response
|
|
136
|
+
let display = `✓ Replied to **@${targetHandle}**`;
|
|
137
|
+
|
|
138
|
+
if (wasTruncated) {
|
|
139
|
+
display += ` ${warning(`truncated to ${MAX_LENGTH} chars`)}`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (threadContext) {
|
|
143
|
+
display += `\n\n_${threadContext.unreadCount} message${threadContext.unreadCount > 1 ? 's' : ''} marked as read_`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check for more unread
|
|
147
|
+
const remainingThreads = await store.getInbox(myHandle);
|
|
148
|
+
const stillUnread = remainingThreads.filter(t => t.unread > 0 && t.handle !== targetHandle);
|
|
149
|
+
|
|
150
|
+
if (stillUnread.length > 0) {
|
|
151
|
+
const nextHandle = stillUnread[0].handle;
|
|
152
|
+
const totalUnread = stillUnread.reduce((sum, t) => sum + t.unread, 0);
|
|
153
|
+
display += `\n\n📬 ${totalUnread} more unread from ${stillUnread.map(t => `@${t.handle}`).slice(0, 3).join(', ')}`;
|
|
154
|
+
display += `\n_Say \`vibe reply "message"\` to reply to @${nextHandle}_`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Build response with actions
|
|
158
|
+
const response = { display };
|
|
159
|
+
|
|
160
|
+
// Suggest follow-up actions
|
|
161
|
+
response.actions = formatActions(actions.afterDm(targetHandle));
|
|
162
|
+
|
|
163
|
+
return response;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
module.exports = { definition, handler };
|
package/tools/report.js
CHANGED
package/tools/request.js
CHANGED
|
@@ -64,9 +64,16 @@ async function handler(args) {
|
|
|
64
64
|
if (args.claim) {
|
|
65
65
|
// Post a claim as a special entry
|
|
66
66
|
try {
|
|
67
|
+
// Include auth token for API authorization
|
|
68
|
+
const authToken = config.getAuthToken();
|
|
69
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
70
|
+
if (authToken) {
|
|
71
|
+
headers['Authorization'] = `Bearer ${authToken}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
67
74
|
const response = await fetch(`${apiUrl}/api/board`, {
|
|
68
75
|
method: 'POST',
|
|
69
|
-
headers
|
|
76
|
+
headers,
|
|
70
77
|
body: JSON.stringify({
|
|
71
78
|
author: myHandle,
|
|
72
79
|
category: 'claim',
|
|
@@ -103,9 +110,16 @@ async function handler(args) {
|
|
|
103
110
|
const fullContent = `${content}${bountyText}`;
|
|
104
111
|
|
|
105
112
|
try {
|
|
113
|
+
// Include auth token for API authorization
|
|
114
|
+
const authToken = config.getAuthToken();
|
|
115
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
116
|
+
if (authToken) {
|
|
117
|
+
headers['Authorization'] = `Bearer ${authToken}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
106
120
|
const response = await fetch(`${apiUrl}/api/board`, {
|
|
107
121
|
method: 'POST',
|
|
108
|
-
headers
|
|
122
|
+
headers,
|
|
109
123
|
body: JSON.stringify({
|
|
110
124
|
author: myHandle,
|
|
111
125
|
category: 'request',
|
|
@@ -136,7 +150,7 @@ async function handler(args) {
|
|
|
136
150
|
// Browse requests
|
|
137
151
|
try {
|
|
138
152
|
const limit = Math.min(args.limit || 10, 30);
|
|
139
|
-
|
|
153
|
+
const url = `${apiUrl}/api/board?limit=${limit * 2}&category=request`;
|
|
140
154
|
|
|
141
155
|
// Also get claims to show status
|
|
142
156
|
const claimsUrl = `${apiUrl}/api/board?limit=50&category=claim`;
|