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.
Files changed (235) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +280 -47
  3. package/auto-update.js +10 -15
  4. package/config.js +36 -31
  5. package/crypto.js +1 -6
  6. package/debug.js +12 -0
  7. package/discord.js +19 -19
  8. package/eslint.config.js +54 -0
  9. package/index.js +217 -207
  10. package/intelligence/index.js +2 -9
  11. package/intelligence/infer.js +10 -16
  12. package/intelligence/patterns.js +23 -18
  13. package/intelligence/proactive.js +16 -15
  14. package/intelligence/serendipity.js +57 -20
  15. package/memory.js +13 -8
  16. package/migrate-v2.js +72 -0
  17. package/notification-emitter.js +2 -2
  18. package/notify.js +39 -14
  19. package/package.json +28 -29
  20. package/post-install.js +141 -0
  21. package/presence.js +2 -2
  22. package/prompts.js +5 -9
  23. package/protocol/index.js +123 -87
  24. package/protocol/telegram-commands.js +36 -37
  25. package/store/api.js +358 -529
  26. package/store/local.js +9 -10
  27. package/store/profiles.js +48 -192
  28. package/store/reservations.js +2 -9
  29. package/store/skills.js +69 -71
  30. package/store/sqlite.js +355 -0
  31. package/test-skills-bootstrap.js +20 -0
  32. package/test-v2-integration.js +385 -0
  33. package/tools/_actions.js +48 -387
  34. package/tools/_connection-queue.js +45 -56
  35. package/tools/_discovery-enhanced.js +52 -57
  36. package/tools/_discovery.js +87 -185
  37. package/tools/{l2-status.js → _experimental/l2-status.js} +68 -70
  38. package/tools/{shipback.js → _experimental/shipback.js} +4 -3
  39. package/tools/_proactive-discovery.js +60 -73
  40. package/tools/_shared/index.js +41 -64
  41. package/tools/admin-inbox.js +10 -15
  42. package/tools/agents.js +1 -1
  43. package/tools/artifact-create.js +13 -23
  44. package/tools/artifact-view.js +4 -4
  45. package/tools/{_deprecated/back.js → back.js} +1 -1
  46. package/tools/bye.js +3 -5
  47. package/tools/consent.js +2 -2
  48. package/tools/context.js +9 -10
  49. package/tools/crossword.js +3 -2
  50. package/tools/discover.js +94 -356
  51. package/tools/dm.js +27 -86
  52. package/tools/doctor.js +12 -41
  53. package/tools/drawing.js +34 -20
  54. package/tools/echo.js +11 -11
  55. package/tools/feed.js +30 -58
  56. package/tools/follow.js +64 -187
  57. package/tools/{_deprecated/forget.js → forget.js} +4 -7
  58. package/tools/game.js +144 -48
  59. package/tools/handoff.js +6 -8
  60. package/tools/help.js +3 -3
  61. package/tools/idea.js +15 -27
  62. package/tools/inbox.js +121 -293
  63. package/tools/init.js +54 -151
  64. package/tools/invite.js +8 -21
  65. package/tools/migrate.js +27 -24
  66. package/tools/multiplayer-game.js +50 -40
  67. package/tools/{_deprecated/mute.js → mute.js} +4 -3
  68. package/tools/notifications.js +58 -48
  69. package/tools/observe.js +12 -15
  70. package/tools/onboarding.js +8 -11
  71. package/tools/open.js +13 -144
  72. package/tools/party-game.js +23 -12
  73. package/tools/patterns.js +2 -1
  74. package/tools/ping.js +5 -7
  75. package/tools/react.js +28 -30
  76. package/tools/{_deprecated/recall.js → recall.js} +5 -10
  77. package/tools/release.js +4 -2
  78. package/tools/{_deprecated/remember.js → remember.js} +4 -6
  79. package/tools/report.js +2 -2
  80. package/tools/request.js +6 -26
  81. package/tools/reserve.js +1 -1
  82. package/tools/session-fork.js +97 -0
  83. package/tools/session-save.js +109 -0
  84. package/tools/settings.js +30 -99
  85. package/tools/ship.js +74 -56
  86. package/tools/{_deprecated/skills-exchange.js → skills-exchange.js} +38 -39
  87. package/tools/social-inbox.js +22 -28
  88. package/tools/social-post.js +24 -27
  89. package/tools/solo-game.js +54 -46
  90. package/tools/start.js +14 -148
  91. package/tools/status.js +21 -68
  92. package/tools/submit.js +4 -2
  93. package/tools/suggest-tags.js +36 -33
  94. package/tools/summarize.js +19 -16
  95. package/tools/tag-suggestions.js +72 -73
  96. package/tools/test.js +1 -1
  97. package/tools/{_deprecated/tictactoe.js → tictactoe.js} +26 -26
  98. package/tools/token.js +4 -4
  99. package/tools/update.js +1 -2
  100. package/tools/watch.js +132 -112
  101. package/tools/who.js +20 -40
  102. package/tools/{_deprecated/wordassociation.js → wordassociation.js} +23 -20
  103. package/tools/workshop-buddy.js +52 -53
  104. package/tools/x-mentions.js +0 -1
  105. package/tools/x-reply.js +0 -1
  106. package/twitter.js +14 -20
  107. package/version.json +8 -10
  108. package/webhook-runner.js +132 -0
  109. package/auth-store.js +0 -148
  110. package/bridges/bridge-monitor.js +0 -388
  111. package/bridges/discord-bot.js +0 -431
  112. package/bridges/farcaster.js +0 -299
  113. package/bridges/telegram.js +0 -261
  114. package/bridges/webhook-health.js +0 -420
  115. package/bridges/webhook-server.js +0 -437
  116. package/bridges/whatsapp.js +0 -441
  117. package/bridges/x-webhook.js +0 -423
  118. package/games/arcade.js +0 -406
  119. package/games/chess.js +0 -451
  120. package/games/colorguess.js +0 -343
  121. package/games/crossword-words.js +0 -171
  122. package/games/crossword.js +0 -461
  123. package/games/drawing.js +0 -347
  124. package/games/gameroulette.js +0 -300
  125. package/games/gamerouter.js +0 -336
  126. package/games/gamestatus.js +0 -337
  127. package/games/guessnumber.js +0 -209
  128. package/games/hangman.js +0 -279
  129. package/games/memory.js +0 -338
  130. package/games/multiplayer-tictactoe.js +0 -389
  131. package/games/pixelart.js +0 -399
  132. package/games/quickduel.js +0 -354
  133. package/games/riddle.js +0 -371
  134. package/games/rockpaperscissors.js +0 -291
  135. package/games/snake.js +0 -406
  136. package/games/storybuilder.js +0 -343
  137. package/games/tictactoe.js +0 -345
  138. package/games/twentyquestions.js +0 -286
  139. package/games/twotruths.js +0 -207
  140. package/games/werewolf.js +0 -508
  141. package/games/wordassociation.js +0 -247
  142. package/games/wordchain.js +0 -135
  143. package/intelligence/interests.js +0 -369
  144. package/setup.js +0 -480
  145. package/smart-inbox.js +0 -276
  146. package/tools/_deprecated/auto-suggest-connections.js +0 -304
  147. package/tools/_deprecated/bootstrap-skills.js +0 -231
  148. package/tools/_deprecated/bridge-dashboard.js +0 -342
  149. package/tools/_deprecated/bridge-health.js +0 -400
  150. package/tools/_deprecated/bridge-live.js +0 -384
  151. package/tools/_deprecated/bridges.js +0 -383
  152. package/tools/_deprecated/colorguess.js +0 -281
  153. package/tools/_deprecated/discover-insights.js +0 -379
  154. package/tools/_deprecated/discover-momentum.js +0 -256
  155. package/tools/_deprecated/discovery-analytics.js +0 -345
  156. package/tools/_deprecated/discovery-auto-suggest.js +0 -275
  157. package/tools/_deprecated/discovery-bootstrap.js +0 -267
  158. package/tools/_deprecated/discovery-daily.js +0 -375
  159. package/tools/_deprecated/discovery-dashboard.js +0 -385
  160. package/tools/_deprecated/discovery-digest.js +0 -314
  161. package/tools/_deprecated/discovery-hub.js +0 -357
  162. package/tools/_deprecated/discovery-insights.js +0 -384
  163. package/tools/_deprecated/discovery-momentum.js +0 -281
  164. package/tools/_deprecated/discovery-monitor.js +0 -319
  165. package/tools/_deprecated/discovery-proactive.js +0 -300
  166. package/tools/_deprecated/draw.js +0 -317
  167. package/tools/_deprecated/farcaster.js +0 -307
  168. package/tools/_deprecated/games-catalog.js +0 -376
  169. package/tools/_deprecated/games.js +0 -313
  170. package/tools/_deprecated/guessnumber.js +0 -194
  171. package/tools/_deprecated/hangman.js +0 -129
  172. package/tools/_deprecated/multiplayer-tictactoe.js +0 -303
  173. package/tools/_deprecated/riddle.js +0 -240
  174. package/tools/_deprecated/run-bootstrap.js +0 -69
  175. package/tools/_deprecated/skills-analytics.js +0 -349
  176. package/tools/_deprecated/skills-bootstrap.js +0 -301
  177. package/tools/_deprecated/skills-dashboard.js +0 -268
  178. package/tools/_deprecated/skills.js +0 -380
  179. package/tools/_deprecated/smart-intro.js +0 -353
  180. package/tools/_deprecated/storybuilder.js +0 -331
  181. package/tools/_deprecated/telegram-bot.js +0 -183
  182. package/tools/_deprecated/telegram-setup.js +0 -214
  183. package/tools/_deprecated/twentyquestions.js +0 -143
  184. package/tools/_shared.js +0 -234
  185. package/tools/_work-context.js +0 -338
  186. package/tools/_work-context.manual-test.js +0 -199
  187. package/tools/_work-context.test.js +0 -260
  188. package/tools/activity.js +0 -220
  189. package/tools/agent-treasury.js +0 -288
  190. package/tools/analytics.js +0 -191
  191. package/tools/approve.js +0 -197
  192. package/tools/arcade.js +0 -173
  193. package/tools/artifacts-price.js +0 -107
  194. package/tools/ask-expert.js +0 -160
  195. package/tools/available.js +0 -120
  196. package/tools/become-expert.js +0 -150
  197. package/tools/broadcast.js +0 -325
  198. package/tools/chat.js +0 -202
  199. package/tools/collaborative-drawing.js +0 -286
  200. package/tools/connection-status.js +0 -178
  201. package/tools/earnings.js +0 -126
  202. package/tools/friends.js +0 -207
  203. package/tools/genesis.js +0 -233
  204. package/tools/gig-browse.js +0 -206
  205. package/tools/gig-complete.js +0 -144
  206. package/tools/health.js +0 -87
  207. package/tools/leaderboard.js +0 -117
  208. package/tools/lib/git-apply.js +0 -206
  209. package/tools/lib/git-bundle.js +0 -407
  210. package/tools/mint.js +0 -377
  211. package/tools/plan.js +0 -225
  212. package/tools/profile.js +0 -219
  213. package/tools/proof-of-work.js +0 -144
  214. package/tools/pulse.js +0 -218
  215. package/tools/reply.js +0 -166
  216. package/tools/reputation.js +0 -175
  217. package/tools/schedule.js +0 -367
  218. package/tools/search-messages.js +0 -123
  219. package/tools/session.js +0 -467
  220. package/tools/session_price.js +0 -128
  221. package/tools/smart-check.js +0 -201
  222. package/tools/social-processor.js +0 -445
  223. package/tools/streak.js +0 -147
  224. package/tools/stuck.js +0 -297
  225. package/tools/subscribe.js +0 -148
  226. package/tools/subscriptions.js +0 -134
  227. package/tools/tip.js +0 -193
  228. package/tools/wallet.js +0 -269
  229. package/tools/webhook-test.js +0 -388
  230. package/tools/withdraw.js +0 -145
  231. package/tools/work-summary.js +0 -96
  232. package/tools/workshop.js +0 -327
  233. /package/tools/{l2-bridge.js → _experimental/l2-bridge.js} +0 -0
  234. /package/tools/{l2.js → _experimental/l2.js} +0 -0
  235. /package/tools/{_deprecated/away.js → away.js} +0 -0
@@ -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, // emoji, recap, etc.
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.toLowerCase()
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: (payload) => {
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: (payload) => {
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: (payload) => {
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: (payload) => {
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('game', { game, state }, {
234
- idempotencyKey: options.idempotencyKey || generateIdempotencyKey('game', game),
235
- ...options
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('handoff', { task, context }, {
267
- idempotencyKey: options.idempotencyKey || generateIdempotencyKey('handoff', task),
268
- ...options
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 'code':
311
- return formatCodePayload(payload);
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 = (i) => b[i] || '·';
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 = template === 'guide' ? '📘' : template === 'learning' ? '💡' : template === 'workspace' ? '🗂️' : '📦';
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
- // Code helpers
496
- createCodePayload,
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_') // Italic
101
- .replace(/`(.*?)`/g, '`$1`'); // Code
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
+ };