slashvibe-mcp 0.3.21 → 0.3.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +280 -47
- package/auto-update.js +10 -15
- package/config.js +36 -31
- package/crypto.js +1 -6
- package/debug.js +12 -0
- package/discord.js +19 -19
- package/eslint.config.js +54 -0
- package/index.js +217 -207
- package/intelligence/index.js +2 -9
- package/intelligence/infer.js +10 -16
- package/intelligence/patterns.js +23 -18
- package/intelligence/proactive.js +16 -15
- package/intelligence/serendipity.js +57 -20
- package/memory.js +13 -8
- package/migrate-v2.js +72 -0
- package/notification-emitter.js +2 -2
- package/notify.js +39 -14
- package/package.json +28 -29
- package/post-install.js +141 -0
- package/presence.js +2 -2
- package/prompts.js +5 -9
- package/protocol/index.js +123 -87
- package/protocol/telegram-commands.js +36 -37
- package/store/api.js +358 -529
- package/store/local.js +9 -10
- package/store/profiles.js +48 -192
- package/store/reservations.js +2 -9
- package/store/skills.js +69 -71
- package/store/sqlite.js +355 -0
- package/test-skills-bootstrap.js +20 -0
- package/test-v2-integration.js +385 -0
- package/tools/_actions.js +48 -387
- package/tools/_connection-queue.js +45 -56
- package/tools/_discovery-enhanced.js +52 -57
- package/tools/_discovery.js +87 -185
- package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
- package/tools/{shipback.js → _experimental/shipback.js} +4 -3
- package/tools/_proactive-discovery.js +60 -73
- package/tools/_shared/index.js +41 -64
- package/tools/admin-inbox.js +10 -15
- package/tools/agents.js +1 -1
- package/tools/artifact-create.js +13 -23
- package/tools/artifact-view.js +4 -4
- package/tools/{_deprecated/back.js → back.js} +1 -1
- package/tools/bye.js +3 -5
- package/tools/consent.js +2 -2
- package/tools/context.js +9 -10
- package/tools/crossword.js +3 -2
- package/tools/discover.js +94 -356
- package/tools/dm.js +27 -86
- package/tools/doctor.js +12 -41
- package/tools/drawing.js +34 -20
- package/tools/echo.js +11 -11
- package/tools/feed.js +30 -58
- package/tools/follow.js +64 -187
- package/tools/{_deprecated/forget.js → forget.js} +4 -7
- package/tools/game.js +144 -48
- package/tools/handoff.js +6 -8
- package/tools/help.js +3 -3
- package/tools/idea.js +15 -27
- package/tools/inbox.js +121 -293
- package/tools/init.js +54 -151
- package/tools/invite.js +8 -21
- package/tools/migrate.js +27 -24
- package/tools/multiplayer-game.js +50 -40
- package/tools/{_deprecated/mute.js → mute.js} +4 -3
- package/tools/notifications.js +58 -48
- package/tools/observe.js +12 -15
- package/tools/onboarding.js +8 -11
- package/tools/open.js +13 -144
- package/tools/party-game.js +23 -12
- package/tools/patterns.js +2 -1
- package/tools/ping.js +5 -7
- package/tools/react.js +28 -30
- package/tools/{_deprecated/recall.js → recall.js} +5 -10
- package/tools/release.js +4 -2
- package/tools/{_deprecated/remember.js → remember.js} +4 -6
- package/tools/report.js +2 -2
- package/tools/request.js +6 -26
- package/tools/reserve.js +1 -1
- package/tools/session-fork.js +97 -0
- package/tools/session-save.js +109 -0
- package/tools/settings.js +30 -99
- package/tools/ship.js +74 -56
- package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
- package/tools/social-inbox.js +22 -28
- package/tools/social-post.js +24 -27
- package/tools/solo-game.js +54 -46
- package/tools/start.js +14 -148
- package/tools/status.js +21 -68
- package/tools/submit.js +4 -2
- package/tools/suggest-tags.js +36 -33
- package/tools/summarize.js +19 -16
- package/tools/tag-suggestions.js +72 -73
- package/tools/test.js +1 -1
- package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
- package/tools/token.js +4 -4
- package/tools/update.js +1 -2
- package/tools/watch.js +132 -112
- package/tools/who.js +20 -40
- package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
- package/tools/workshop-buddy.js +52 -53
- package/tools/x-mentions.js +0 -1
- package/tools/x-reply.js +0 -1
- package/twitter.js +14 -20
- package/version.json +8 -10
- package/webhook-runner.js +132 -0
- package/auth-store.js +0 -148
- package/bridges/bridge-monitor.js +0 -388
- package/bridges/discord-bot.js +0 -431
- package/bridges/farcaster.js +0 -299
- package/bridges/telegram.js +0 -261
- package/bridges/webhook-health.js +0 -420
- package/bridges/webhook-server.js +0 -437
- package/bridges/whatsapp.js +0 -441
- package/bridges/x-webhook.js +0 -423
- package/games/arcade.js +0 -406
- package/games/chess.js +0 -451
- package/games/colorguess.js +0 -343
- package/games/crossword-words.js +0 -171
- package/games/crossword.js +0 -461
- package/games/drawing.js +0 -347
- package/games/gameroulette.js +0 -300
- package/games/gamerouter.js +0 -336
- package/games/gamestatus.js +0 -337
- package/games/guessnumber.js +0 -209
- package/games/hangman.js +0 -279
- package/games/memory.js +0 -338
- package/games/multiplayer-tictactoe.js +0 -389
- package/games/pixelart.js +0 -399
- package/games/quickduel.js +0 -354
- package/games/riddle.js +0 -371
- package/games/rockpaperscissors.js +0 -291
- package/games/snake.js +0 -406
- package/games/storybuilder.js +0 -343
- package/games/tictactoe.js +0 -345
- package/games/twentyquestions.js +0 -286
- package/games/twotruths.js +0 -207
- package/games/werewolf.js +0 -508
- package/games/wordassociation.js +0 -247
- package/games/wordchain.js +0 -135
- package/intelligence/interests.js +0 -369
- package/setup.js +0 -480
- package/smart-inbox.js +0 -276
- package/tools/_deprecated/auto-suggest-connections.js +0 -304
- package/tools/_deprecated/bootstrap-skills.js +0 -231
- package/tools/_deprecated/bridge-dashboard.js +0 -342
- package/tools/_deprecated/bridge-health.js +0 -400
- package/tools/_deprecated/bridge-live.js +0 -384
- package/tools/_deprecated/bridges.js +0 -383
- package/tools/_deprecated/colorguess.js +0 -281
- package/tools/_deprecated/discover-insights.js +0 -379
- package/tools/_deprecated/discover-momentum.js +0 -256
- package/tools/_deprecated/discovery-analytics.js +0 -345
- package/tools/_deprecated/discovery-auto-suggest.js +0 -275
- package/tools/_deprecated/discovery-bootstrap.js +0 -267
- package/tools/_deprecated/discovery-daily.js +0 -375
- package/tools/_deprecated/discovery-dashboard.js +0 -385
- package/tools/_deprecated/discovery-digest.js +0 -314
- package/tools/_deprecated/discovery-hub.js +0 -357
- package/tools/_deprecated/discovery-insights.js +0 -384
- package/tools/_deprecated/discovery-momentum.js +0 -281
- package/tools/_deprecated/discovery-monitor.js +0 -319
- package/tools/_deprecated/discovery-proactive.js +0 -300
- package/tools/_deprecated/draw.js +0 -317
- package/tools/_deprecated/farcaster.js +0 -307
- package/tools/_deprecated/games-catalog.js +0 -376
- package/tools/_deprecated/games.js +0 -313
- package/tools/_deprecated/guessnumber.js +0 -194
- package/tools/_deprecated/hangman.js +0 -129
- package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
- package/tools/_deprecated/riddle.js +0 -240
- package/tools/_deprecated/run-bootstrap.js +0 -69
- package/tools/_deprecated/skills-analytics.js +0 -349
- package/tools/_deprecated/skills-bootstrap.js +0 -301
- package/tools/_deprecated/skills-dashboard.js +0 -268
- package/tools/_deprecated/skills.js +0 -380
- package/tools/_deprecated/smart-intro.js +0 -353
- package/tools/_deprecated/storybuilder.js +0 -331
- package/tools/_deprecated/telegram-bot.js +0 -183
- package/tools/_deprecated/telegram-setup.js +0 -214
- package/tools/_deprecated/twentyquestions.js +0 -143
- package/tools/_shared.js +0 -234
- package/tools/_work-context.js +0 -338
- package/tools/_work-context.manual-test.js +0 -199
- package/tools/_work-context.test.js +0 -260
- package/tools/activity.js +0 -220
- package/tools/agent-treasury.js +0 -288
- package/tools/analytics.js +0 -191
- package/tools/approve.js +0 -197
- package/tools/arcade.js +0 -173
- package/tools/artifacts-price.js +0 -107
- package/tools/ask-expert.js +0 -160
- package/tools/available.js +0 -120
- package/tools/become-expert.js +0 -150
- package/tools/broadcast.js +0 -325
- package/tools/chat.js +0 -202
- package/tools/collaborative-drawing.js +0 -286
- package/tools/connection-status.js +0 -178
- package/tools/earnings.js +0 -126
- package/tools/friends.js +0 -207
- package/tools/genesis.js +0 -233
- package/tools/gig-browse.js +0 -206
- package/tools/gig-complete.js +0 -144
- package/tools/health.js +0 -87
- package/tools/leaderboard.js +0 -117
- package/tools/lib/git-apply.js +0 -206
- package/tools/lib/git-bundle.js +0 -407
- package/tools/mint.js +0 -377
- package/tools/plan.js +0 -225
- package/tools/profile.js +0 -219
- package/tools/proof-of-work.js +0 -144
- package/tools/pulse.js +0 -218
- package/tools/reply.js +0 -166
- package/tools/reputation.js +0 -175
- package/tools/schedule.js +0 -367
- package/tools/search-messages.js +0 -123
- package/tools/session.js +0 -467
- package/tools/session_price.js +0 -128
- package/tools/smart-check.js +0 -201
- package/tools/social-processor.js +0 -445
- package/tools/streak.js +0 -147
- package/tools/stuck.js +0 -297
- package/tools/subscribe.js +0 -148
- package/tools/subscriptions.js +0 -134
- package/tools/tip.js +0 -193
- package/tools/wallet.js +0 -269
- package/tools/webhook-test.js +0 -388
- package/tools/withdraw.js +0 -145
- package/tools/work-summary.js +0 -96
- package/tools/workshop.js +0 -327
- /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
- /package/tools/{l2.js → _experimental/l2.js} +0 -0
- /package/tools/{_deprecated/away.js → away.js} +0 -0
package/post-install.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-install script for slashvibe-mcp
|
|
5
|
+
* Sets up MCP server configuration and CLAUDE.md for Claude Code
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = path.dirname(__filename);
|
|
15
|
+
|
|
16
|
+
const HOME = homedir();
|
|
17
|
+
const CLAUDE_CONFIG_DIR = path.join(HOME, '.config', 'claude-code');
|
|
18
|
+
const MCP_CONFIG_PATH = path.join(CLAUDE_CONFIG_DIR, 'mcp.json');
|
|
19
|
+
const CLAUDE_MD_DIR = path.join(HOME, '.claude');
|
|
20
|
+
const CLAUDE_MD_PATH = path.join(CLAUDE_MD_DIR, 'CLAUDE.md');
|
|
21
|
+
const VIBE_MARKER = '## /vibe - Terminal-Native Social';
|
|
22
|
+
|
|
23
|
+
async function setup() {
|
|
24
|
+
console.log('\n📦 Setting up /vibe...\n');
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Ensure config directory exists
|
|
28
|
+
await fs.mkdir(CLAUDE_CONFIG_DIR, { recursive: true });
|
|
29
|
+
await fs.mkdir(CLAUDE_MD_DIR, { recursive: true });
|
|
30
|
+
|
|
31
|
+
// Read existing MCP config or create new
|
|
32
|
+
let config = { mcpServers: {} };
|
|
33
|
+
try {
|
|
34
|
+
const existing = await fs.readFile(MCP_CONFIG_PATH, 'utf-8');
|
|
35
|
+
config = JSON.parse(existing);
|
|
36
|
+
if (!config.mcpServers) config.mcpServers = {};
|
|
37
|
+
} catch (error) {
|
|
38
|
+
// File doesn't exist, use default
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Use npx to run slashvibe-mcp — works regardless of install location
|
|
42
|
+
config.mcpServers.vibe = {
|
|
43
|
+
command: 'npx',
|
|
44
|
+
args: ['-y', 'slashvibe-mcp']
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Write config
|
|
48
|
+
await fs.writeFile(MCP_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
49
|
+
|
|
50
|
+
// Inject CLAUDE.md template for dashboard mode
|
|
51
|
+
await injectClaudeMd();
|
|
52
|
+
|
|
53
|
+
console.log('✅ /vibe MCP server configured\n');
|
|
54
|
+
console.log('Next steps:');
|
|
55
|
+
console.log(' 1. Restart Claude Code');
|
|
56
|
+
console.log(' 2. Run: vibe init @yourusername\n');
|
|
57
|
+
console.log('📖 Docs: https://slashvibe.dev\n');
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('⚠️ Setup incomplete:', error.message);
|
|
60
|
+
console.error('\nManual setup:');
|
|
61
|
+
console.error(' Add to ~/.config/claude-code/mcp.json:\n');
|
|
62
|
+
console.error(' {');
|
|
63
|
+
console.error(' "mcpServers": {');
|
|
64
|
+
console.error(' "vibe": {');
|
|
65
|
+
console.error(' "command": "npx",');
|
|
66
|
+
console.error(' "args": ["-y", "slashvibe-mcp"]');
|
|
67
|
+
console.error(' }');
|
|
68
|
+
console.error(' }');
|
|
69
|
+
console.error(' }\n');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Inject /vibe CLAUDE.md template for dashboard mode and hint handling
|
|
75
|
+
* - If CLAUDE.md doesn't exist, create it with template
|
|
76
|
+
* - If exists but no /vibe section, append template
|
|
77
|
+
* - If /vibe section exists, update it with latest template
|
|
78
|
+
*/
|
|
79
|
+
async function injectClaudeMd() {
|
|
80
|
+
try {
|
|
81
|
+
// Find the template relative to this script
|
|
82
|
+
// When installed via npm: ../dashboard/VIBE_CLAUDE_MD_TEMPLATE.md
|
|
83
|
+
const templatePath = path.join(__dirname, '..', 'dashboard', 'VIBE_CLAUDE_MD_TEMPLATE.md');
|
|
84
|
+
|
|
85
|
+
let template;
|
|
86
|
+
try {
|
|
87
|
+
template = await fs.readFile(templatePath, 'utf-8');
|
|
88
|
+
} catch (e) {
|
|
89
|
+
// Template not found (might be dev environment), skip injection
|
|
90
|
+
console.log('ℹ️ CLAUDE.md template not found, skipping dashboard setup');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if CLAUDE.md exists
|
|
95
|
+
let existingContent = '';
|
|
96
|
+
try {
|
|
97
|
+
existingContent = await fs.readFile(CLAUDE_MD_PATH, 'utf-8');
|
|
98
|
+
} catch (e) {
|
|
99
|
+
// File doesn't exist, will create new
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (existingContent.includes(VIBE_MARKER)) {
|
|
103
|
+
// /vibe section exists - replace it with updated template
|
|
104
|
+
// Find the section and replace it
|
|
105
|
+
const startIndex = existingContent.indexOf(VIBE_MARKER);
|
|
106
|
+
|
|
107
|
+
// Find the next ## header (or end of file)
|
|
108
|
+
const afterMarker = existingContent.substring(startIndex + VIBE_MARKER.length);
|
|
109
|
+
const nextSectionMatch = afterMarker.match(/\n## (?!\/vibe)/);
|
|
110
|
+
|
|
111
|
+
let endIndex;
|
|
112
|
+
if (nextSectionMatch) {
|
|
113
|
+
endIndex = startIndex + VIBE_MARKER.length + nextSectionMatch.index;
|
|
114
|
+
} else {
|
|
115
|
+
endIndex = existingContent.length;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Replace the section
|
|
119
|
+
const beforeSection = existingContent.substring(0, startIndex);
|
|
120
|
+
const afterSection = existingContent.substring(endIndex);
|
|
121
|
+
const newContent = beforeSection + template + afterSection;
|
|
122
|
+
|
|
123
|
+
await fs.writeFile(CLAUDE_MD_PATH, newContent);
|
|
124
|
+
console.log('✅ CLAUDE.md updated with latest /vibe dashboard');
|
|
125
|
+
} else if (existingContent) {
|
|
126
|
+
// CLAUDE.md exists but no /vibe section - append
|
|
127
|
+
const newContent = existingContent + '\n\n' + template;
|
|
128
|
+
await fs.writeFile(CLAUDE_MD_PATH, newContent);
|
|
129
|
+
console.log('✅ /vibe dashboard added to CLAUDE.md');
|
|
130
|
+
} else {
|
|
131
|
+
// No CLAUDE.md - create with template
|
|
132
|
+
await fs.writeFile(CLAUDE_MD_PATH, template);
|
|
133
|
+
console.log('✅ CLAUDE.md created with /vibe dashboard');
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
// Non-fatal - log but don't fail installation
|
|
137
|
+
console.log('ℹ️ Could not update CLAUDE.md:', error.message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
setup();
|
package/presence.js
CHANGED
|
@@ -60,7 +60,7 @@ async function sendHeartbeat() {
|
|
|
60
60
|
const handle = config.getHandle();
|
|
61
61
|
const one_liner = config.getOneLiner();
|
|
62
62
|
if (handle) {
|
|
63
|
-
store.heartbeat(handle, one_liner || '');
|
|
63
|
+
store.heartbeat(handle, one_liner || '', null, 'mcp');
|
|
64
64
|
|
|
65
65
|
// Check for notifications (runs in background, non-blocking)
|
|
66
66
|
notify.checkAll(store).catch(() => {});
|
|
@@ -84,7 +84,7 @@ async function forceHeartbeat() {
|
|
|
84
84
|
|
|
85
85
|
// Send heartbeat
|
|
86
86
|
const one_liner = config.getOneLiner();
|
|
87
|
-
await store.heartbeat(handle, one_liner || '');
|
|
87
|
+
await store.heartbeat(handle, one_liner || '', null, 'mcp');
|
|
88
88
|
|
|
89
89
|
return { success: true, handle };
|
|
90
90
|
}
|
package/prompts.js
CHANGED
|
@@ -25,7 +25,7 @@ function log(prompt, resolution) {
|
|
|
25
25
|
tool: resolution.tool || null,
|
|
26
26
|
action: resolution.action || null,
|
|
27
27
|
target: resolution.target || null, // @handle, channel, etc.
|
|
28
|
-
transform: resolution.transform || null
|
|
28
|
+
transform: resolution.transform || null // emoji, recap, etc.
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
try {
|
|
@@ -44,10 +44,7 @@ function getRecent(limit = 50) {
|
|
|
44
44
|
try {
|
|
45
45
|
if (!fs.existsSync(PROMPTS_FILE)) return [];
|
|
46
46
|
|
|
47
|
-
const lines = fs.readFileSync(PROMPTS_FILE, 'utf8')
|
|
48
|
-
.trim()
|
|
49
|
-
.split('\n')
|
|
50
|
-
.filter(Boolean);
|
|
47
|
+
const lines = fs.readFileSync(PROMPTS_FILE, 'utf8').trim().split('\n').filter(Boolean);
|
|
51
48
|
|
|
52
49
|
return lines
|
|
53
50
|
.slice(-limit)
|
|
@@ -68,7 +65,8 @@ function extractPatterns() {
|
|
|
68
65
|
|
|
69
66
|
for (const p of prompts) {
|
|
70
67
|
// Normalize: lowercase, replace @handles with @*, replace quoted strings with "*"
|
|
71
|
-
const normalized = p.prompt
|
|
68
|
+
const normalized = p.prompt
|
|
69
|
+
.toLowerCase()
|
|
72
70
|
.replace(/@\w+/g, '@*')
|
|
73
71
|
.replace(/"[^"]+"/g, '"*"')
|
|
74
72
|
.replace(/'[^']+'/g, "'*'")
|
|
@@ -125,9 +123,7 @@ function getAnonymizedPatterns() {
|
|
|
125
123
|
pattern,
|
|
126
124
|
frequency: count,
|
|
127
125
|
// Remove any potentially identifying info
|
|
128
|
-
normalized: pattern
|
|
129
|
-
.replace(/\d+/g, 'N')
|
|
130
|
-
.replace(/[a-f0-9]{8,}/gi, 'HASH')
|
|
126
|
+
normalized: pattern.replace(/\d+/g, 'N').replace(/[a-f0-9]{8,}/gi, 'HASH')
|
|
131
127
|
}));
|
|
132
128
|
}
|
|
133
129
|
|
package/protocol/index.js
CHANGED
|
@@ -34,7 +34,7 @@ const PROTOCOL_VERSION = '0.1.0';
|
|
|
34
34
|
const GAME_SCHEMA = {
|
|
35
35
|
type: 'game',
|
|
36
36
|
required: ['game', 'state'],
|
|
37
|
-
validate:
|
|
37
|
+
validate: payload => {
|
|
38
38
|
if (!payload.game || typeof payload.game !== 'string') {
|
|
39
39
|
return { valid: false, error: 'Missing or invalid game name' };
|
|
40
40
|
}
|
|
@@ -65,7 +65,7 @@ const GAME_SCHEMA = {
|
|
|
65
65
|
const HANDOFF_SCHEMA = {
|
|
66
66
|
type: 'handoff',
|
|
67
67
|
required: ['task', 'context'],
|
|
68
|
-
validate:
|
|
68
|
+
validate: payload => {
|
|
69
69
|
if (!payload.task || typeof payload.task !== 'string') {
|
|
70
70
|
return { valid: false, error: 'Missing or invalid task type' };
|
|
71
71
|
}
|
|
@@ -90,7 +90,7 @@ const HANDOFF_SCHEMA = {
|
|
|
90
90
|
const ACK_SCHEMA = {
|
|
91
91
|
type: 'ack',
|
|
92
92
|
required: ['replyTo', 'status'],
|
|
93
|
-
validate:
|
|
93
|
+
validate: payload => {
|
|
94
94
|
if (!payload.replyTo || typeof payload.replyTo !== 'string') {
|
|
95
95
|
return { valid: false, error: 'Missing or invalid replyTo' };
|
|
96
96
|
}
|
|
@@ -119,7 +119,7 @@ const ACK_SCHEMA = {
|
|
|
119
119
|
const ARTIFACT_SCHEMA = {
|
|
120
120
|
type: 'artifact',
|
|
121
121
|
required: ['artifactId', 'slug', 'title', 'template', 'url'],
|
|
122
|
-
validate:
|
|
122
|
+
validate: payload => {
|
|
123
123
|
if (!payload.artifactId || typeof payload.artifactId !== 'string') {
|
|
124
124
|
return { valid: false, error: 'Missing or invalid artifactId' };
|
|
125
125
|
}
|
|
@@ -139,11 +139,56 @@ const ARTIFACT_SCHEMA = {
|
|
|
139
139
|
}
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Agent wire schema — For agent-to-agent communication on the wire
|
|
144
|
+
*
|
|
145
|
+
* Used when external agents (Clawdbot, @seth) communicate via /vibe.
|
|
146
|
+
* AIRC-signed for identity verification.
|
|
147
|
+
*
|
|
148
|
+
* Example:
|
|
149
|
+
* {
|
|
150
|
+
* type: 'agent',
|
|
151
|
+
* version: '0.1.0',
|
|
152
|
+
* action: 'session_sync',
|
|
153
|
+
* idempotencyKey: 'agent_seth_session_abc',
|
|
154
|
+
* source: { platform: 'telegram', gateway: 'clawdbot' },
|
|
155
|
+
* context: {
|
|
156
|
+
* session: 'NODE logistics',
|
|
157
|
+
* mood: 'shipping',
|
|
158
|
+
* file: 'node365/submit/page.tsx'
|
|
159
|
+
* }
|
|
160
|
+
* }
|
|
161
|
+
*/
|
|
162
|
+
const AGENT_SCHEMA = {
|
|
163
|
+
type: 'agent',
|
|
164
|
+
required: ['action'],
|
|
165
|
+
validate: payload => {
|
|
166
|
+
if (!payload.action || typeof payload.action !== 'string') {
|
|
167
|
+
return { valid: false, error: 'Missing or invalid action' };
|
|
168
|
+
}
|
|
169
|
+
const validActions = [
|
|
170
|
+
'session_sync', // Sync session state to /vibe presence
|
|
171
|
+
'event_subscribe', // Subscribe to event pushes
|
|
172
|
+
'memory_query', // Query local memory
|
|
173
|
+
'memory_store', // Store observation
|
|
174
|
+
'identity_verify', // AIRC identity verification
|
|
175
|
+
'heartbeat', // Agent heartbeat
|
|
176
|
+
'capability_announce' // Announce agent capabilities
|
|
177
|
+
];
|
|
178
|
+
// Allow unknown actions for forward compat
|
|
179
|
+
if (!validActions.includes(payload.action)) {
|
|
180
|
+
return { valid: true, unknown_action: true };
|
|
181
|
+
}
|
|
182
|
+
return { valid: true };
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
142
186
|
const SCHEMAS = {
|
|
143
187
|
game: GAME_SCHEMA,
|
|
144
188
|
handoff: HANDOFF_SCHEMA,
|
|
145
189
|
ack: ACK_SCHEMA,
|
|
146
|
-
artifact: ARTIFACT_SCHEMA
|
|
190
|
+
artifact: ARTIFACT_SCHEMA,
|
|
191
|
+
agent: AGENT_SCHEMA
|
|
147
192
|
};
|
|
148
193
|
|
|
149
194
|
// ============ PROTOCOL FUNCTIONS ============
|
|
@@ -230,10 +275,14 @@ function generateIdempotencyKey(prefix, context = '') {
|
|
|
230
275
|
* @returns {Object} - Game payload
|
|
231
276
|
*/
|
|
232
277
|
function createGamePayload(game, state, options = {}) {
|
|
233
|
-
return createPayload(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
278
|
+
return createPayload(
|
|
279
|
+
'game',
|
|
280
|
+
{ game, state },
|
|
281
|
+
{
|
|
282
|
+
idempotencyKey: options.idempotencyKey || generateIdempotencyKey('game', game),
|
|
283
|
+
...options
|
|
284
|
+
}
|
|
285
|
+
);
|
|
237
286
|
}
|
|
238
287
|
|
|
239
288
|
/**
|
|
@@ -263,10 +312,14 @@ function createTicTacToePayload(board, turn, moves, winner = null) {
|
|
|
263
312
|
* @returns {Object} - Handoff payload
|
|
264
313
|
*/
|
|
265
314
|
function createHandoffPayload(task, context, options = {}) {
|
|
266
|
-
return createPayload(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
315
|
+
return createPayload(
|
|
316
|
+
'handoff',
|
|
317
|
+
{ task, context },
|
|
318
|
+
{
|
|
319
|
+
idempotencyKey: options.idempotencyKey || generateIdempotencyKey('handoff', task),
|
|
320
|
+
...options
|
|
321
|
+
}
|
|
322
|
+
);
|
|
270
323
|
}
|
|
271
324
|
|
|
272
325
|
// ============ ACK HELPERS ============
|
|
@@ -307,8 +360,8 @@ function formatPayload(payload) {
|
|
|
307
360
|
return formatAckPayload(payload);
|
|
308
361
|
case 'artifact':
|
|
309
362
|
return formatArtifactPayload(payload);
|
|
310
|
-
case '
|
|
311
|
-
return
|
|
363
|
+
case 'agent':
|
|
364
|
+
return formatAgentPayload(payload);
|
|
312
365
|
default:
|
|
313
366
|
return `📦 _${payload.type} payload_`;
|
|
314
367
|
}
|
|
@@ -320,7 +373,7 @@ function formatGamePayload(payload) {
|
|
|
320
373
|
|
|
321
374
|
if (game === 'tictactoe' && state.board) {
|
|
322
375
|
const b = state.board;
|
|
323
|
-
const cell =
|
|
376
|
+
const cell = i => b[i] || '·';
|
|
324
377
|
return `🎮 **Tic-Tac-Toe** (move ${state.moves || '?'})
|
|
325
378
|
\`\`\`
|
|
326
379
|
${cell(0)} │ ${cell(1)} │ ${cell(2)}
|
|
@@ -359,77 +412,10 @@ function formatAckPayload(payload) {
|
|
|
359
412
|
return `${icon} Acknowledged: ${payload.replyTo} (${status})`;
|
|
360
413
|
}
|
|
361
414
|
|
|
362
|
-
/**
|
|
363
|
-
* Format a code snippet payload for display
|
|
364
|
-
*/
|
|
365
|
-
function formatCodePayload(payload) {
|
|
366
|
-
const lang = payload.language || '';
|
|
367
|
-
const filename = payload.filename || null;
|
|
368
|
-
const code = payload.code || '';
|
|
369
|
-
const description = payload.description || null;
|
|
370
|
-
|
|
371
|
-
let display = '📝 **Code Snippet**';
|
|
372
|
-
if (filename) {
|
|
373
|
-
display += ` — \`${filename}\``;
|
|
374
|
-
}
|
|
375
|
-
if (lang) {
|
|
376
|
-
display += ` (${lang})`;
|
|
377
|
-
}
|
|
378
|
-
display += '\n';
|
|
379
|
-
|
|
380
|
-
if (description) {
|
|
381
|
-
display += `> ${description}\n`;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
display += `\`\`\`${lang}\n${code}\n\`\`\``;
|
|
385
|
-
|
|
386
|
-
return display;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Create a code snippet payload
|
|
391
|
-
* @param {string} code - The code content
|
|
392
|
-
* @param {object} options - Options
|
|
393
|
-
* @param {string} [options.language] - Programming language
|
|
394
|
-
* @param {string} [options.filename] - Original filename
|
|
395
|
-
* @param {string} [options.description] - Brief description
|
|
396
|
-
*/
|
|
397
|
-
function createCodePayload(code, options = {}) {
|
|
398
|
-
return {
|
|
399
|
-
type: 'code',
|
|
400
|
-
version: PROTOCOL_VERSION,
|
|
401
|
-
code,
|
|
402
|
-
language: options.language || detectLanguage(code),
|
|
403
|
-
filename: options.filename || null,
|
|
404
|
-
description: options.description || null,
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Simple language detection based on content patterns
|
|
410
|
-
*/
|
|
411
|
-
function detectLanguage(code) {
|
|
412
|
-
if (!code) return '';
|
|
413
|
-
|
|
414
|
-
// Check for common patterns
|
|
415
|
-
if (code.includes('import React') || code.includes('useState') || code.includes('useEffect')) return 'jsx';
|
|
416
|
-
if (code.includes('import ') && code.includes(' from ')) return 'javascript';
|
|
417
|
-
if (code.includes('async function') || code.includes('await ')) return 'javascript';
|
|
418
|
-
if (code.includes('def ') && code.includes(':')) return 'python';
|
|
419
|
-
if (code.includes('func ') && code.includes('()')) return 'go';
|
|
420
|
-
if (code.includes('fn ') && code.includes('->')) return 'rust';
|
|
421
|
-
if (code.includes('SELECT ') || code.includes('INSERT INTO')) return 'sql';
|
|
422
|
-
if (code.includes('<!DOCTYPE') || code.includes('<html')) return 'html';
|
|
423
|
-
if (code.includes('{') && code.includes(':') && code.includes(';')) return 'css';
|
|
424
|
-
if (code.startsWith('{') && code.endsWith('}')) return 'json';
|
|
425
|
-
if (code.startsWith('#!') && code.includes('/bin/')) return 'bash';
|
|
426
|
-
|
|
427
|
-
return '';
|
|
428
|
-
}
|
|
429
|
-
|
|
430
415
|
function formatArtifactPayload(payload) {
|
|
431
416
|
const template = payload.template || 'artifact';
|
|
432
|
-
const templateIcon =
|
|
417
|
+
const templateIcon =
|
|
418
|
+
template === 'guide' ? '📘' : template === 'learning' ? '💡' : template === 'workspace' ? '🗂️' : '📦';
|
|
433
419
|
|
|
434
420
|
let display = `${templateIcon} **${payload.title}**\n`;
|
|
435
421
|
|
|
@@ -470,6 +456,56 @@ function createArtifactPayload(artifact) {
|
|
|
470
456
|
});
|
|
471
457
|
}
|
|
472
458
|
|
|
459
|
+
// ============ AGENT HELPERS ============
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Create an agent wire payload
|
|
463
|
+
* @param {string} action - Agent action (session_sync, event_subscribe, etc.)
|
|
464
|
+
* @param {object} data - Action-specific data
|
|
465
|
+
* @param {object} [options] - Options (idempotencyKey, source)
|
|
466
|
+
* @returns {object} - Agent payload
|
|
467
|
+
*/
|
|
468
|
+
function createAgentPayload(action, data = {}, options = {}) {
|
|
469
|
+
const payload = {
|
|
470
|
+
action,
|
|
471
|
+
...data
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
if (options.source) {
|
|
475
|
+
payload.source = options.source;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return createPayload('agent', payload, {
|
|
479
|
+
idempotencyKey: options.idempotencyKey || generateIdempotencyKey('agent', action),
|
|
480
|
+
...options
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Format agent payload for display
|
|
486
|
+
*/
|
|
487
|
+
function formatAgentPayload(payload) {
|
|
488
|
+
const action = payload.action || 'unknown';
|
|
489
|
+
const source = payload.source
|
|
490
|
+
? ` (via ${payload.source.platform || payload.source.gateway || 'unknown'})`
|
|
491
|
+
: '';
|
|
492
|
+
|
|
493
|
+
switch (action) {
|
|
494
|
+
case 'session_sync':
|
|
495
|
+
return `🤖 **Session sync**${source}\n> ${payload.context?.session || 'active'}`;
|
|
496
|
+
case 'event_subscribe':
|
|
497
|
+
return `🔔 **Event subscription**${source}\n> ${(payload.events || []).join(', ')}`;
|
|
498
|
+
case 'heartbeat':
|
|
499
|
+
return `💓 **Agent heartbeat**${source}`;
|
|
500
|
+
case 'identity_verify':
|
|
501
|
+
return `🔑 **Identity verification**${source}`;
|
|
502
|
+
case 'capability_announce':
|
|
503
|
+
return `📡 **Capabilities**${source}\n> ${(payload.capabilities || []).join(', ')}`;
|
|
504
|
+
default:
|
|
505
|
+
return `🤖 **Agent: ${action}**${source}`;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
473
509
|
module.exports = {
|
|
474
510
|
PROTOCOL_VERSION,
|
|
475
511
|
|
|
@@ -492,8 +528,8 @@ module.exports = {
|
|
|
492
528
|
// Artifact helpers
|
|
493
529
|
createArtifactPayload,
|
|
494
530
|
|
|
495
|
-
//
|
|
496
|
-
|
|
531
|
+
// Agent helpers
|
|
532
|
+
createAgentPayload,
|
|
497
533
|
|
|
498
534
|
// Schemas (for extension)
|
|
499
535
|
SCHEMAS
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* /vibe Telegram Command Processor
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Executes /vibe commands received via Telegram bot.
|
|
5
5
|
* Maps Telegram users to /vibe handles and executes core protocol functions.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const { dm } = require('../tools/dm');
|
|
9
|
-
const { status } = require('../tools/status');
|
|
9
|
+
const { status } = require('../tools/status');
|
|
10
10
|
const { who } = require('../tools/who');
|
|
11
11
|
const { ship } = require('../tools/ship');
|
|
12
12
|
const config = require('../config');
|
|
@@ -17,7 +17,7 @@ const config = require('../config');
|
|
|
17
17
|
async function processVibeCommand(command, params, telegramUser) {
|
|
18
18
|
// Map Telegram user to /vibe handle
|
|
19
19
|
const vibeHandle = mapTelegramUserToHandle(telegramUser);
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
if (!vibeHandle) {
|
|
22
22
|
return `❌ Telegram account not linked to /vibe. Contact admin to link @${telegramUser.username || telegramUser.first_name} to a /vibe handle.`;
|
|
23
23
|
}
|
|
@@ -26,20 +26,19 @@ async function processVibeCommand(command, params, telegramUser) {
|
|
|
26
26
|
switch (command) {
|
|
27
27
|
case 'status':
|
|
28
28
|
return await executeStatus(params.mood, params.note, vibeHandle);
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
case 'who':
|
|
31
31
|
return await executeWho();
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
case 'ship':
|
|
34
34
|
return await executeShip(params.message, vibeHandle);
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
case 'dm':
|
|
37
37
|
return await executeDM(params.handle, params.message, vibeHandle);
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
default:
|
|
40
40
|
return `❌ Unknown command: ${command}`;
|
|
41
41
|
}
|
|
42
|
-
|
|
43
42
|
} catch (error) {
|
|
44
43
|
console.error(`Telegram command error [${command}]:`, error);
|
|
45
44
|
return `❌ Command failed: ${error.message}`;
|
|
@@ -52,7 +51,7 @@ async function processVibeCommand(command, params, telegramUser) {
|
|
|
52
51
|
function mapTelegramUserToHandle(telegramUser) {
|
|
53
52
|
const cfg = config.load();
|
|
54
53
|
const telegramMappings = cfg.telegram_user_mappings || {};
|
|
55
|
-
|
|
54
|
+
|
|
56
55
|
// Try username first, then user ID
|
|
57
56
|
const telegramKey = telegramUser.username || telegramUser.id.toString();
|
|
58
57
|
return telegramMappings[telegramKey] || null;
|
|
@@ -65,19 +64,19 @@ async function executeStatus(mood, note, handle) {
|
|
|
65
64
|
if (!mood) {
|
|
66
65
|
return '❌ Need a mood. Try: `/status shipping "building the future"`';
|
|
67
66
|
}
|
|
68
|
-
|
|
67
|
+
|
|
69
68
|
const validMoods = ['shipping', 'debugging', 'deep', 'afk', 'celebrating', 'pairing'];
|
|
70
69
|
if (!validMoods.includes(mood)) {
|
|
71
70
|
return `❌ Invalid mood. Use: ${validMoods.join(', ')}`;
|
|
72
71
|
}
|
|
73
|
-
|
|
72
|
+
|
|
74
73
|
// Execute the status update
|
|
75
74
|
const result = await status.handler({ mood, note });
|
|
76
|
-
|
|
75
|
+
|
|
77
76
|
if (result.error) {
|
|
78
77
|
return `❌ ${result.error}`;
|
|
79
78
|
}
|
|
80
|
-
|
|
79
|
+
|
|
81
80
|
return `✅ Status updated: **${handle}** is ${mood}${note ? ` - "${note}"` : ''}`;
|
|
82
81
|
}
|
|
83
82
|
|
|
@@ -86,33 +85,33 @@ async function executeStatus(mood, note, handle) {
|
|
|
86
85
|
*/
|
|
87
86
|
async function executeWho() {
|
|
88
87
|
const result = await who.handler({});
|
|
89
|
-
|
|
88
|
+
|
|
90
89
|
if (result.error) {
|
|
91
90
|
return `❌ ${result.error}`;
|
|
92
91
|
}
|
|
93
|
-
|
|
92
|
+
|
|
94
93
|
// Convert display format to Telegram-friendly
|
|
95
94
|
let response = result.display;
|
|
96
|
-
|
|
95
|
+
|
|
97
96
|
// Replace markdown formatting for Telegram
|
|
98
97
|
response = response
|
|
99
98
|
.replace(/\*\*(.*?)\*\*/g, '*$1*') // Bold
|
|
100
|
-
.replace(/_(.*?)_/g, '_$1_')
|
|
101
|
-
.replace(/`(.*?)`/g, '`$1`');
|
|
102
|
-
|
|
99
|
+
.replace(/_(.*?)_/g, '_$1_') // Italic
|
|
100
|
+
.replace(/`(.*?)`/g, '`$1`'); // Code
|
|
101
|
+
|
|
103
102
|
return response;
|
|
104
103
|
}
|
|
105
104
|
|
|
106
105
|
/**
|
|
107
|
-
* Execute ship command
|
|
106
|
+
* Execute ship command
|
|
108
107
|
*/
|
|
109
108
|
async function executeShip(message, handle) {
|
|
110
109
|
const result = await ship.handler({ what: message });
|
|
111
|
-
|
|
110
|
+
|
|
112
111
|
if (result.error) {
|
|
113
112
|
return `❌ ${result.error}`;
|
|
114
113
|
}
|
|
115
|
-
|
|
114
|
+
|
|
116
115
|
return `🚀 Shipped! ${message ? `"${message}"` : 'Great work!'} has been announced to /vibe.`;
|
|
117
116
|
}
|
|
118
117
|
|
|
@@ -123,23 +122,23 @@ async function executeDM(targetHandle, message, fromHandle) {
|
|
|
123
122
|
if (!targetHandle) {
|
|
124
123
|
return '❌ Need target handle. Try: `/dm @alice "hey there!"`';
|
|
125
124
|
}
|
|
126
|
-
|
|
125
|
+
|
|
127
126
|
if (!message) {
|
|
128
127
|
return '❌ Need message content.';
|
|
129
128
|
}
|
|
130
|
-
|
|
129
|
+
|
|
131
130
|
// Remove @ if present
|
|
132
131
|
const cleanHandle = targetHandle.replace('@', '');
|
|
133
|
-
|
|
134
|
-
const result = await dm.handler({
|
|
135
|
-
to: cleanHandle,
|
|
136
|
-
message: message
|
|
132
|
+
|
|
133
|
+
const result = await dm.handler({
|
|
134
|
+
to: cleanHandle,
|
|
135
|
+
message: message
|
|
137
136
|
});
|
|
138
|
-
|
|
137
|
+
|
|
139
138
|
if (result.error) {
|
|
140
139
|
return `❌ ${result.error}`;
|
|
141
140
|
}
|
|
142
|
-
|
|
141
|
+
|
|
143
142
|
return `📨 DM sent to @${cleanHandle}: "${message}"`;
|
|
144
143
|
}
|
|
145
144
|
|
|
@@ -148,19 +147,19 @@ async function executeDM(targetHandle, message, fromHandle) {
|
|
|
148
147
|
*/
|
|
149
148
|
async function linkTelegramUser(telegramUser, vibeHandle) {
|
|
150
149
|
const cfg = config.load();
|
|
151
|
-
|
|
150
|
+
|
|
152
151
|
if (!cfg.telegram_user_mappings) {
|
|
153
152
|
cfg.telegram_user_mappings = {};
|
|
154
153
|
}
|
|
155
|
-
|
|
154
|
+
|
|
156
155
|
// Store both username and ID for flexibility
|
|
157
156
|
if (telegramUser.username) {
|
|
158
157
|
cfg.telegram_user_mappings[telegramUser.username] = vibeHandle;
|
|
159
158
|
}
|
|
160
159
|
cfg.telegram_user_mappings[telegramUser.id.toString()] = vibeHandle;
|
|
161
|
-
|
|
160
|
+
|
|
162
161
|
config.save(cfg);
|
|
163
|
-
|
|
162
|
+
|
|
164
163
|
return {
|
|
165
164
|
success: true,
|
|
166
165
|
message: `Linked Telegram user ${telegramUser.username || telegramUser.id} to /vibe handle @${vibeHandle}`
|
|
@@ -180,13 +179,13 @@ function getTelegramMappings() {
|
|
|
180
179
|
*/
|
|
181
180
|
function unlinkTelegramUser(telegramKey) {
|
|
182
181
|
const cfg = config.load();
|
|
183
|
-
|
|
182
|
+
|
|
184
183
|
if (cfg.telegram_user_mappings && cfg.telegram_user_mappings[telegramKey]) {
|
|
185
184
|
delete cfg.telegram_user_mappings[telegramKey];
|
|
186
185
|
config.save(cfg);
|
|
187
186
|
return { success: true, message: `Unlinked ${telegramKey}` };
|
|
188
187
|
}
|
|
189
|
-
|
|
188
|
+
|
|
190
189
|
return { success: false, message: `No mapping found for ${telegramKey}` };
|
|
191
190
|
}
|
|
192
191
|
|
|
@@ -196,4 +195,4 @@ module.exports = {
|
|
|
196
195
|
unlinkTelegramUser,
|
|
197
196
|
getTelegramMappings,
|
|
198
197
|
mapTelegramUserToHandle
|
|
199
|
-
};
|
|
198
|
+
};
|