slashvibe-mcp 0.2.2 → 0.2.4
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 +1 -0
- package/analytics.js +107 -0
- package/config.js +174 -3
- package/index.js +163 -34
- package/intelligence/index.js +45 -0
- package/intelligence/infer.js +316 -0
- package/intelligence/interests.js +369 -0
- package/intelligence/patterns.js +651 -0
- package/intelligence/proactive.js +358 -0
- package/intelligence/serendipity.js +306 -0
- package/notification-emitter.js +77 -0
- package/notify.js +141 -18
- package/package.json +14 -6
- package/presence.js +5 -1
- package/protocol/index.js +88 -1
- package/protocol/telegram-commands.js +199 -0
- package/store/api.js +469 -29
- package/store/index.js +7 -7
- package/store/local.js +67 -11
- package/store/profiles.js +435 -0
- package/store/reservations.js +321 -0
- package/store/skills.js +378 -0
- package/tools/_actions.js +491 -22
- package/tools/_connection-queue.js +257 -0
- package/tools/_discovery-enhanced.js +290 -0
- package/tools/_discovery.js +439 -0
- package/tools/_proactive-discovery.js +301 -0
- package/tools/_shared/index.js +64 -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/admin-inbox.js +218 -0
- package/tools/agent-treasury.js +288 -0
- package/tools/agents.js +122 -0
- package/tools/analytics.js +191 -0
- package/tools/approve.js +197 -0
- package/tools/arcade.js +173 -0
- package/tools/artifact-create.js +247 -0
- package/tools/artifact-view.js +174 -0
- package/tools/artifacts-price.js +107 -0
- package/tools/ask-expert.js +160 -0
- package/tools/auto-suggest-connections.js +304 -0
- package/tools/away.js +68 -0
- package/tools/back.js +51 -0
- package/tools/become-expert.js +150 -0
- package/tools/bootstrap-skills.js +231 -0
- package/tools/bridge-dashboard.js +342 -0
- package/tools/bridge-health.js +400 -0
- package/tools/bridge-live.js +384 -0
- package/tools/bridges.js +383 -0
- package/tools/broadcast.js +286 -0
- package/tools/bye.js +4 -0
- package/tools/chat.js +202 -0
- package/tools/collaborative-drawing.js +286 -0
- package/tools/colorguess.js +281 -0
- package/tools/crossword.js +369 -0
- package/tools/discover-insights.js +379 -0
- package/tools/discover-momentum.js +256 -0
- package/tools/discover.js +675 -0
- package/tools/discovery-analytics.js +345 -0
- package/tools/discovery-auto-suggest.js +275 -0
- package/tools/discovery-bootstrap.js +267 -0
- package/tools/discovery-daily.js +375 -0
- package/tools/discovery-dashboard.js +385 -0
- package/tools/discovery-digest.js +314 -0
- package/tools/discovery-hub.js +357 -0
- package/tools/discovery-insights.js +384 -0
- package/tools/discovery-momentum.js +281 -0
- package/tools/discovery-monitor.js +319 -0
- package/tools/discovery-proactive.js +300 -0
- package/tools/dm.js +84 -14
- package/tools/draw.js +317 -0
- package/tools/drawing.js +310 -0
- package/tools/earnings.js +126 -0
- package/tools/echo.js +16 -0
- package/tools/farcaster.js +307 -0
- package/tools/feed.js +215 -0
- package/tools/follow.js +224 -0
- package/tools/friends.js +192 -0
- package/tools/game.js +218 -110
- package/tools/games-catalog.js +376 -0
- package/tools/games.js +313 -0
- package/tools/genesis.js +233 -0
- package/tools/gig-browse.js +206 -0
- package/tools/gig-complete.js +139 -0
- package/tools/guessnumber.js +194 -0
- package/tools/hangman.js +129 -0
- package/tools/help.js +269 -0
- package/tools/idea.js +217 -0
- package/tools/inbox.js +291 -25
- package/tools/init.js +657 -33
- package/tools/insights.js +123 -0
- package/tools/invite.js +142 -21
- package/tools/l2-bridge.js +272 -0
- package/tools/l2-status.js +217 -0
- package/tools/l2.js +206 -0
- package/tools/migrate.js +156 -0
- package/tools/mint.js +377 -0
- package/tools/multiplayer-game.js +275 -0
- package/tools/multiplayer-tictactoe.js +303 -0
- package/tools/mute.js +97 -0
- package/tools/notifications.js +415 -0
- package/tools/observe.js +200 -0
- package/tools/onboarding.js +147 -0
- package/tools/open.js +52 -3
- package/tools/party-game.js +314 -0
- package/tools/plan.js +225 -0
- package/tools/presence-agent.js +167 -0
- package/tools/profile.js +219 -0
- package/tools/proof-of-work.js +139 -0
- package/tools/pulse.js +218 -0
- package/tools/react.js +4 -0
- package/tools/release.js +83 -0
- package/tools/report.js +109 -0
- package/tools/reputation.js +175 -0
- package/tools/request.js +231 -0
- package/tools/reservations.js +116 -0
- package/tools/reserve.js +111 -0
- package/tools/riddle.js +240 -0
- package/tools/run-bootstrap.js +69 -0
- package/tools/schedule.js +367 -0
- package/tools/session.js +420 -0
- package/tools/session_price.js +128 -0
- package/tools/settings.js +200 -0
- package/tools/ship.js +188 -0
- package/tools/shipback.js +326 -0
- package/tools/skills-analytics.js +349 -0
- package/tools/skills-bootstrap.js +301 -0
- package/tools/skills-dashboard.js +268 -0
- package/tools/skills-exchange.js +342 -0
- package/tools/skills.js +380 -0
- package/tools/smart-intro.js +353 -0
- package/tools/social-inbox.js +326 -69
- package/tools/social-post.js +251 -66
- package/tools/social-processor.js +445 -0
- package/tools/solo-game.js +390 -0
- package/tools/start.js +296 -81
- package/tools/status.js +53 -6
- package/tools/storybuilder.js +331 -0
- package/tools/stuck.js +297 -0
- package/tools/subscribe.js +148 -0
- package/tools/subscriptions.js +134 -0
- package/tools/suggest-tags.js +184 -0
- package/tools/tag-suggestions.js +257 -0
- package/tools/telegram-bot.js +183 -0
- package/tools/telegram-setup.js +214 -0
- package/tools/tictactoe.js +155 -0
- package/tools/tip.js +120 -0
- package/tools/token.js +103 -0
- package/tools/twentyquestions.js +143 -0
- package/tools/update.js +1 -1
- package/tools/wallet.js +127 -0
- package/tools/watch.js +157 -0
- package/tools/webhook-test.js +388 -0
- package/tools/who.js +118 -25
- package/tools/withdraw.js +145 -0
- package/tools/wordassociation.js +247 -0
- package/tools/work-summary.js +96 -0
- package/tools/workshop-buddy.js +394 -0
- package/tools/workshop.js +327 -0
- package/version.json +12 -3
- package/tools/board.js +0 -130
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe_work_summary — Standalone tool for explicit context gathering
|
|
3
|
+
*
|
|
4
|
+
* Use this when you need to:
|
|
5
|
+
* - Draft a message about what you're working on
|
|
6
|
+
* - Share your progress with someone
|
|
7
|
+
* - Get context for composing a "ship" post
|
|
8
|
+
*
|
|
9
|
+
* This tool returns structured data about:
|
|
10
|
+
* - Git state (branch, recent commits, changed files)
|
|
11
|
+
* - Project info (name, type)
|
|
12
|
+
* - Pre-formatted suggestions ready for messages/presence
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { gatherWorkContext } = require('./_work-context');
|
|
16
|
+
|
|
17
|
+
const definition = {
|
|
18
|
+
name: 'vibe_work_summary',
|
|
19
|
+
description: 'Get a summary of what you\'re working on (git state, project info). Use before composing messages about your work.',
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: 'object',
|
|
22
|
+
properties: {
|
|
23
|
+
detail_level: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
enum: ['brief', 'detailed'],
|
|
26
|
+
description: 'brief = one-liner, detailed = include commits/files'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
async function handler(args) {
|
|
33
|
+
const { detail_level = 'brief' } = args;
|
|
34
|
+
|
|
35
|
+
// Gather all context
|
|
36
|
+
const context = gatherWorkContext();
|
|
37
|
+
|
|
38
|
+
// Build display based on detail level
|
|
39
|
+
let display = '## Work Context\n\n';
|
|
40
|
+
|
|
41
|
+
// Project info
|
|
42
|
+
display += `**Project:** ${context.project.name}`;
|
|
43
|
+
if (context.project.type !== 'unknown') {
|
|
44
|
+
display += ` (${context.project.type})`;
|
|
45
|
+
}
|
|
46
|
+
display += '\n';
|
|
47
|
+
|
|
48
|
+
// Git info
|
|
49
|
+
if (context.git) {
|
|
50
|
+
display += `**Branch:** ${context.git.branch}\n`;
|
|
51
|
+
|
|
52
|
+
if (detail_level === 'detailed') {
|
|
53
|
+
// Show recent commits
|
|
54
|
+
if (context.git.recentCommits.length > 0) {
|
|
55
|
+
display += '\n**Recent commits:**\n';
|
|
56
|
+
context.git.recentCommits.forEach(c => {
|
|
57
|
+
display += `- \`${c.hash}\` ${c.message}\n`;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Show changed files
|
|
62
|
+
if (context.git.changedFiles.length > 0) {
|
|
63
|
+
display += '\n**Changed files:**\n';
|
|
64
|
+
context.git.changedFiles.forEach(f => {
|
|
65
|
+
display += `- ${f}\n`;
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Uncommitted changes indicator
|
|
70
|
+
if (context.git.hasUncommitted) {
|
|
71
|
+
display += '\n⚠️ _Uncommitted changes_\n';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
display += '_Not in a git repository_\n';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Suggestions
|
|
79
|
+
display += '\n---\n\n';
|
|
80
|
+
display += '**Ready to use:**\n';
|
|
81
|
+
display += `- Brief: "${context.suggestions.brief}"\n`;
|
|
82
|
+
if (detail_level === 'detailed' && context.suggestions.detailed !== context.suggestions.brief) {
|
|
83
|
+
display += `- Detailed: "${context.suggestions.detailed}"\n`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
display += '\n_Use these in messages, status updates, or ship posts._';
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
display,
|
|
90
|
+
// Structured data for Claude to use programmatically
|
|
91
|
+
...context,
|
|
92
|
+
usage_hint: 'Use suggestions.brief for messages, suggestions.detailed for updates.'
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = { definition, handler };
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vibe workshop-buddy — Find Your Perfect Workshop Partner
|
|
3
|
+
*
|
|
4
|
+
* Pairs users based on complementary skills and interests to form
|
|
5
|
+
* productive workshop partnerships. Unlike general discovery,
|
|
6
|
+
* this focuses on active collaboration potential.
|
|
7
|
+
*
|
|
8
|
+
* Commands:
|
|
9
|
+
* - workshop-buddy find — Find your ideal workshop partner
|
|
10
|
+
* - workshop-buddy offer <skills> — Offer skills to the community
|
|
11
|
+
* - workshop-buddy seeking <skills> — What you're looking for
|
|
12
|
+
* - workshop-buddy matches — See current skill exchange opportunities
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const config = require('../config');
|
|
16
|
+
const store = require('../store');
|
|
17
|
+
const userProfiles = require('../store/profiles');
|
|
18
|
+
const { formatTimeAgo, requireInit } = require('./_shared');
|
|
19
|
+
|
|
20
|
+
const definition = {
|
|
21
|
+
name: 'vibe_workshop_buddy',
|
|
22
|
+
description: 'Find workshop partners with complementary skills for collaboration.',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
command: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
enum: ['find', 'offer', 'seeking', 'matches'],
|
|
29
|
+
description: 'Workshop buddy command to run'
|
|
30
|
+
},
|
|
31
|
+
skills: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
description: 'Skills you offer or seek (comma-separated)'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Skill complementarity matrix
|
|
40
|
+
const complementarySkills = {
|
|
41
|
+
'frontend': ['backend', 'design', 'ux'],
|
|
42
|
+
'backend': ['frontend', 'devops', 'database'],
|
|
43
|
+
'design': ['frontend', 'user-research', 'engineering'],
|
|
44
|
+
'ux': ['design', 'frontend', 'user-research'],
|
|
45
|
+
'ai': ['data', 'backend', 'research'],
|
|
46
|
+
'data': ['ai', 'backend', 'analytics'],
|
|
47
|
+
'mobile': ['backend', 'design', 'ux'],
|
|
48
|
+
'devops': ['backend', 'security', 'infrastructure'],
|
|
49
|
+
'product': ['engineering', 'design', 'marketing'],
|
|
50
|
+
'engineering': ['product', 'design', 'backend'],
|
|
51
|
+
'research': ['ai', 'data', 'implementation'],
|
|
52
|
+
'marketing': ['product', 'design', 'content'],
|
|
53
|
+
'content': ['marketing', 'design', 'writing'],
|
|
54
|
+
'security': ['devops', 'backend', 'infrastructure'],
|
|
55
|
+
'infrastructure': ['devops', 'backend', 'security'],
|
|
56
|
+
'analytics': ['data', 'product', 'research'],
|
|
57
|
+
'user-research': ['ux', 'product', 'design'],
|
|
58
|
+
'writing': ['content', 'marketing', 'editing'],
|
|
59
|
+
'business': ['product', 'marketing', 'strategy'],
|
|
60
|
+
'strategy': ['business', 'product', 'leadership']
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Calculate workshop collaboration potential
|
|
64
|
+
function calculateBuddyScore(user1, user2) {
|
|
65
|
+
let score = 0;
|
|
66
|
+
const reasons = [];
|
|
67
|
+
const collaborationPotential = [];
|
|
68
|
+
|
|
69
|
+
const user1Skills = (user1.tags || []).concat(user1.interests || []);
|
|
70
|
+
const user2Skills = (user2.tags || []).concat(user2.interests || []);
|
|
71
|
+
|
|
72
|
+
// High-value complementary skills
|
|
73
|
+
for (const skill1 of user1Skills) {
|
|
74
|
+
const complementaries = complementarySkills[skill1.toLowerCase()] || [];
|
|
75
|
+
for (const skill2 of user2Skills) {
|
|
76
|
+
if (complementaries.includes(skill2.toLowerCase())) {
|
|
77
|
+
score += 30;
|
|
78
|
+
collaborationPotential.push(`${skill1} + ${skill2}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Project collaboration potential
|
|
84
|
+
if (user1.building && user2.building) {
|
|
85
|
+
const building1 = user1.building.toLowerCase();
|
|
86
|
+
const building2 = user2.building.toLowerCase();
|
|
87
|
+
|
|
88
|
+
// Different but related domains
|
|
89
|
+
const domains = ['ai', 'web', 'mobile', 'fintech', 'healthcare', 'gaming', 'productivity'];
|
|
90
|
+
const user1Domains = domains.filter(d => building1.includes(d));
|
|
91
|
+
const user2Domains = domains.filter(d => building2.includes(d));
|
|
92
|
+
|
|
93
|
+
if (user1Domains.length > 0 && user2Domains.length > 0) {
|
|
94
|
+
const sharedDomains = user1Domains.filter(d => user2Domains.includes(d));
|
|
95
|
+
if (sharedDomains.length > 0) {
|
|
96
|
+
score += 25;
|
|
97
|
+
reasons.push(`Both working in ${sharedDomains.join(', ')}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Experience level balance
|
|
103
|
+
const user1Level = getExperienceLevel(user1);
|
|
104
|
+
const user2Level = getExperienceLevel(user2);
|
|
105
|
+
|
|
106
|
+
if (Math.abs(user1Level - user2Level) <= 1) {
|
|
107
|
+
score += 15;
|
|
108
|
+
reasons.push('Compatible experience levels');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Activity synchronization
|
|
112
|
+
if (user1.lastSeen && user2.lastSeen) {
|
|
113
|
+
const timeDiff = Math.abs(user1.lastSeen - user2.lastSeen);
|
|
114
|
+
const hours = timeDiff / (1000 * 60 * 60);
|
|
115
|
+
if (hours < 6) {
|
|
116
|
+
score += 20;
|
|
117
|
+
reasons.push('Active at similar times');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add collaboration potential to reasons
|
|
122
|
+
if (collaborationPotential.length > 0) {
|
|
123
|
+
reasons.unshift(`Perfect skill combo: ${collaborationPotential.slice(0, 2).join(', ')}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { score, reasons: reasons.slice(0, 3), collaborationPotential };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Estimate experience level from profile
|
|
130
|
+
function getExperienceLevel(user) {
|
|
131
|
+
const skills = (user.tags || []).length;
|
|
132
|
+
const interests = (user.interests || []).length;
|
|
133
|
+
const hasBuilding = user.building ? 1 : 0;
|
|
134
|
+
|
|
135
|
+
return Math.min(5, skills + interests + hasBuilding);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Find workshop buddies
|
|
139
|
+
async function findWorkshopBuddies(myHandle) {
|
|
140
|
+
const myProfile = await userProfiles.getProfile(myHandle);
|
|
141
|
+
const allProfiles = await userProfiles.getAllProfiles();
|
|
142
|
+
|
|
143
|
+
const candidates = allProfiles.filter(p => p.handle !== myHandle);
|
|
144
|
+
const matches = [];
|
|
145
|
+
|
|
146
|
+
for (const candidate of candidates) {
|
|
147
|
+
const match = calculateBuddyScore(myProfile, candidate);
|
|
148
|
+
if (match.score > 20) { // Higher threshold for workshop partnerships
|
|
149
|
+
matches.push({
|
|
150
|
+
handle: candidate.handle,
|
|
151
|
+
score: match.score,
|
|
152
|
+
reasons: match.reasons,
|
|
153
|
+
collaborationPotential: match.collaborationPotential,
|
|
154
|
+
building: candidate.building,
|
|
155
|
+
tags: candidate.tags || [],
|
|
156
|
+
interests: candidate.interests || [],
|
|
157
|
+
lastSeen: candidate.lastSeen,
|
|
158
|
+
experienceLevel: getExperienceLevel(candidate)
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return matches.sort((a, b) => b.score - a.score).slice(0, 3);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Get skill offering/seeking data
|
|
167
|
+
async function getSkillExchange() {
|
|
168
|
+
// This would integrate with a persistent skills marketplace
|
|
169
|
+
// For now, analyze profiles to suggest exchanges
|
|
170
|
+
const allProfiles = await userProfiles.getAllProfiles();
|
|
171
|
+
const exchanges = [];
|
|
172
|
+
|
|
173
|
+
for (const profile of allProfiles) {
|
|
174
|
+
if (profile.tags && profile.tags.length > 0) {
|
|
175
|
+
for (const skill of profile.tags) {
|
|
176
|
+
const seeking = complementarySkills[skill.toLowerCase()] || [];
|
|
177
|
+
if (seeking.length > 0) {
|
|
178
|
+
exchanges.push({
|
|
179
|
+
handle: profile.handle,
|
|
180
|
+
offering: skill,
|
|
181
|
+
seeking: seeking,
|
|
182
|
+
building: profile.building
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return exchanges.slice(0, 10);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function handler(args) {
|
|
193
|
+
const initCheck = requireInit();
|
|
194
|
+
if (initCheck) return initCheck;
|
|
195
|
+
|
|
196
|
+
const myHandle = config.getHandle();
|
|
197
|
+
const command = args.command || 'find';
|
|
198
|
+
|
|
199
|
+
let display = '';
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
switch (command) {
|
|
203
|
+
case 'find': {
|
|
204
|
+
const buddies = await findWorkshopBuddies(myHandle);
|
|
205
|
+
|
|
206
|
+
if (buddies.length === 0) {
|
|
207
|
+
display = `## No Workshop Buddies Found
|
|
208
|
+
|
|
209
|
+
_Need more profiles to find great partnerships._
|
|
210
|
+
|
|
211
|
+
**To find your perfect workshop buddy:**
|
|
212
|
+
1. \`vibe update tags "your-skills"\` (e.g., "frontend, react, typescript")
|
|
213
|
+
2. \`vibe update building "your current project"\`
|
|
214
|
+
3. \`vibe update interests "what excites you"\`
|
|
215
|
+
|
|
216
|
+
**What makes a great workshop buddy:**
|
|
217
|
+
- Complementary skills (frontend + backend)
|
|
218
|
+
- Similar availability/timezone
|
|
219
|
+
- Compatible experience level
|
|
220
|
+
- Shared domain interest`;
|
|
221
|
+
} else {
|
|
222
|
+
display = `## Your Workshop Buddy Matches 🤝\n\n`;
|
|
223
|
+
|
|
224
|
+
for (const buddy of buddies) {
|
|
225
|
+
display += `### **@${buddy.handle}** _(${buddy.score}% match)_\n`;
|
|
226
|
+
display += `${buddy.building || 'Available for collaboration'}\n\n`;
|
|
227
|
+
|
|
228
|
+
display += `**Why you'd work great together:**\n`;
|
|
229
|
+
for (const reason of buddy.reasons) {
|
|
230
|
+
display += `• ${reason}\n`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (buddy.collaborationPotential.length > 0) {
|
|
234
|
+
display += `\n**Skill synergy:** ${buddy.collaborationPotential.slice(0, 2).join(', ')}\n`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
display += `**Their skills:** ${buddy.tags.join(', ')}\n`;
|
|
238
|
+
display += `**Experience level:** ${'⭐'.repeat(buddy.experienceLevel)}\n`;
|
|
239
|
+
display += `_Last seen: ${formatTimeAgo(buddy.lastSeen)}_\n\n`;
|
|
240
|
+
display += `**Reach out:** \`message @${buddy.handle} "Hey! We seem like a great workshop match..."\`\n\n`;
|
|
241
|
+
display += '---\n\n';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
display += `**Next steps:**\n`;
|
|
245
|
+
display += `• Message your top match to start collaborating\n`;
|
|
246
|
+
display += `• Use \`workshop-buddy matches\` to see community skill exchanges\n`;
|
|
247
|
+
display += `• Try \`workshop-buddy seeking "skill"\` to find specific expertise`;
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
case 'offer': {
|
|
253
|
+
if (!args.skills) {
|
|
254
|
+
return { error: 'Specify skills you offer: workshop-buddy offer "frontend, react"' };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Update user tags with offered skills
|
|
258
|
+
const skills = args.skills.split(',').map(s => s.trim().toLowerCase());
|
|
259
|
+
const currentProfile = await userProfiles.getProfile(myHandle);
|
|
260
|
+
const updatedTags = [...new Set([...(currentProfile.tags || []), ...skills])];
|
|
261
|
+
|
|
262
|
+
await userProfiles.updateProfile(myHandle, { tags: updatedTags });
|
|
263
|
+
|
|
264
|
+
display = `## Skills Offered! 🎯
|
|
265
|
+
|
|
266
|
+
**You're now offering:** ${skills.join(', ')}
|
|
267
|
+
|
|
268
|
+
People looking for these skills can find you via:
|
|
269
|
+
- \`discover search "${skills[0]}"\`
|
|
270
|
+
- \`workshop-buddy matches\`
|
|
271
|
+
|
|
272
|
+
**Want to find collaboration opportunities?**
|
|
273
|
+
\`workshop-buddy find\` — Find your ideal workshop partner`;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
case 'seeking': {
|
|
278
|
+
if (!args.skills) {
|
|
279
|
+
return { error: 'Specify skills you need: workshop-buddy seeking "backend, devops"' };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const seekingSkills = args.skills.split(',').map(s => s.trim().toLowerCase());
|
|
283
|
+
const allProfiles = await userProfiles.getAllProfiles();
|
|
284
|
+
|
|
285
|
+
const matches = allProfiles.filter(profile => {
|
|
286
|
+
const theirSkills = (profile.tags || []).map(t => t.toLowerCase());
|
|
287
|
+
return seekingSkills.some(skill => theirSkills.includes(skill)) && profile.handle !== myHandle;
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (matches.length === 0) {
|
|
291
|
+
display = `## No One Found with: ${args.skills}
|
|
292
|
+
|
|
293
|
+
**Try:**
|
|
294
|
+
- \`discover search "${seekingSkills[0]}"\` for broader search
|
|
295
|
+
- \`workshop-buddy matches\` to browse all skill exchanges
|
|
296
|
+
- Post what you're seeking: \`ship "Looking for ${args.skills} expertise"\``;
|
|
297
|
+
} else {
|
|
298
|
+
display = `## People with: ${args.skills}\n\n`;
|
|
299
|
+
|
|
300
|
+
for (const match of matches) {
|
|
301
|
+
const theirMatchingSkills = (match.tags || []).filter(t =>
|
|
302
|
+
seekingSkills.includes(t.toLowerCase())
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
display += `**@${match.handle}**\n`;
|
|
306
|
+
display += `${match.building || 'Available to help'}\n`;
|
|
307
|
+
display += `**Has:** ${theirMatchingSkills.join(', ')}\n`;
|
|
308
|
+
display += `**All skills:** ${(match.tags || []).join(', ')}\n`;
|
|
309
|
+
display += `_Last seen: ${formatTimeAgo(match.lastSeen)}_\n\n`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
display += `**Ready to collaborate?**\n`;
|
|
313
|
+
display += `\`workshop-buddy find\` — See your best overall matches`;
|
|
314
|
+
}
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
case 'matches': {
|
|
319
|
+
const exchanges = await getSkillExchange();
|
|
320
|
+
|
|
321
|
+
if (exchanges.length === 0) {
|
|
322
|
+
display = `## No Skill Exchanges Yet
|
|
323
|
+
|
|
324
|
+
_The skill marketplace is empty._
|
|
325
|
+
|
|
326
|
+
**Start the exchange:**
|
|
327
|
+
1. \`workshop-buddy offer "your-skills"\`
|
|
328
|
+
2. Others can find you via \`workshop-buddy seeking "your-skills"\`
|
|
329
|
+
|
|
330
|
+
**Example skills to offer/seek:**
|
|
331
|
+
- Technical: frontend, backend, mobile, ai, data
|
|
332
|
+
- Design: ui, ux, illustration, branding
|
|
333
|
+
- Business: product, marketing, strategy, writing`;
|
|
334
|
+
} else {
|
|
335
|
+
display = `## Community Skill Exchange 🔄\n\n`;
|
|
336
|
+
|
|
337
|
+
const groupedByOffering = {};
|
|
338
|
+
for (const exchange of exchanges) {
|
|
339
|
+
if (!groupedByOffering[exchange.offering]) {
|
|
340
|
+
groupedByOffering[exchange.offering] = [];
|
|
341
|
+
}
|
|
342
|
+
groupedByOffering[exchange.offering].push(exchange);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
for (const [skill, providers] of Object.entries(groupedByOffering)) {
|
|
346
|
+
display += `### ${skill.toUpperCase()}\n`;
|
|
347
|
+
|
|
348
|
+
for (const provider of providers.slice(0, 3)) {
|
|
349
|
+
display += `**@${provider.handle}** offers ${skill}, seeks: ${provider.seeking.join(', ')}\n`;
|
|
350
|
+
if (provider.building) {
|
|
351
|
+
display += `_Building: ${provider.building}_\n`;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
display += '\n';
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
display += `**Find your perfect exchange:**\n`;
|
|
359
|
+
display += `- \`workshop-buddy seeking "skill"\` to find specific expertise\n`;
|
|
360
|
+
display += `- \`workshop-buddy find\` for AI-matched partners`;
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
default:
|
|
366
|
+
display = `## Workshop Buddy Commands
|
|
367
|
+
|
|
368
|
+
**\`workshop-buddy find\`** — Find your ideal workshop partner
|
|
369
|
+
**\`workshop-buddy offer <skills>\`** — Offer skills to the community
|
|
370
|
+
**\`workshop-buddy seeking <skills>\`** — Find people with specific skills
|
|
371
|
+
**\`workshop-buddy matches\`** — Browse community skill exchanges
|
|
372
|
+
|
|
373
|
+
**Perfect for:**
|
|
374
|
+
- Finding a coding partner with complementary skills
|
|
375
|
+
- Pairing designers with developers
|
|
376
|
+
- Connecting product people with engineers
|
|
377
|
+
- Building cross-functional teams
|
|
378
|
+
|
|
379
|
+
**Examples:**
|
|
380
|
+
\`workshop-buddy offer "frontend, react, typescript"\`
|
|
381
|
+
\`workshop-buddy seeking "backend, python"\``;
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
display = `## Workshop Buddy Error
|
|
385
|
+
|
|
386
|
+
${error.message}
|
|
387
|
+
|
|
388
|
+
Try: \`workshop-buddy\` for available commands`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return { display };
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
module.exports = { definition, handler };
|