slashvibe-mcp 0.3.21 → 0.3.22

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