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
package/tools/dm.js CHANGED
@@ -8,12 +8,13 @@ const memory = require('../memory');
8
8
  const userProfiles = require('../store/profiles');
9
9
  const patterns = require('../intelligence/patterns');
10
10
  const { trackMessage, checkBurst } = require('./summarize');
11
- const { requireInit, normalizeHandle, truncate, warning, fetchRelevantUsers } = require('./_shared');
11
+ const { requireInit, normalizeHandle, truncate, warning, debug } = require('./_shared');
12
12
  const { actions, formatActions } = require('./_actions');
13
13
 
14
14
  const definition = {
15
15
  name: 'vibe_dm',
16
- description: 'Send a direct message to someone. Can include structured payload for games, handoffs, artifact cards, or an instant USDC tip.',
16
+ description:
17
+ 'Send a direct message to someone. Can include structured payload for games, handoffs, or artifact cards.',
17
18
  inputSchema: {
18
19
  type: 'object',
19
20
  properties: {
@@ -27,19 +28,12 @@ const definition = {
27
28
  },
28
29
  artifact_slug: {
29
30
  type: 'string',
30
- description: 'Optional artifact slug to share (e.g., "pizza-guide-abc123"). The artifact will be shown as a rich card.'
31
+ description:
32
+ 'Optional artifact slug to share (e.g., "pizza-guide-abc123"). The artifact will be shown as a rich card.'
31
33
  },
32
34
  payload: {
33
35
  type: 'object',
34
36
  description: 'Optional structured data (game state, code review, handoff, etc.)'
35
- },
36
- reply_to: {
37
- type: 'string',
38
- description: 'Optional: Message ID to reply to (creates a threaded reply)'
39
- },
40
- tip_amount_cents: {
41
- type: 'number',
42
- description: 'Optional: Attach an instant USDC tip (100 = $1, 500 = $5, 1000 = $10)'
43
37
  }
44
38
  },
45
39
  required: ['handle']
@@ -50,7 +44,7 @@ async function handler(args) {
50
44
  const initCheck = requireInit();
51
45
  if (initCheck) return initCheck;
52
46
 
53
- const { handle, message, artifact_slug, payload, reply_to, tip_amount_cents } = args;
47
+ const { handle, message, artifact_slug, payload } = args;
54
48
  const myHandle = config.getHandle();
55
49
  const them = normalizeHandle(handle);
56
50
 
@@ -61,7 +55,7 @@ async function handler(args) {
61
55
  }
62
56
 
63
57
  if (them === myHandle) {
64
- return { display: 'You can\'t DM yourself.' };
58
+ return { display: "You can't DM yourself." };
65
59
  }
66
60
 
67
61
  // Handle artifact sharing
@@ -80,7 +74,7 @@ async function handler(args) {
80
74
  const protocol = require('../protocol');
81
75
  finalPayload = protocol.createArtifactPayload(artifact);
82
76
  } catch (error) {
83
- console.error('Failed to load artifact:', error);
77
+ debug('dm', 'Failed to load artifact:', error);
84
78
  return { display: `Failed to load artifact: ${error.message}` };
85
79
  }
86
80
  }
@@ -95,13 +89,7 @@ async function handler(args) {
95
89
  const wasTruncated = trimmed.length > MAX_LENGTH;
96
90
  const finalMessage = wasTruncated ? trimmed.substring(0, MAX_LENGTH) : trimmed;
97
91
 
98
- // Send typing indicator (shows "typing..." to recipient while message is being sent)
99
- // Non-blocking - we don't wait for this
100
- store.sendTypingIndicator(myHandle, them).catch(() => {});
101
-
102
- const result = await store.sendMessage(myHandle, them, finalMessage || null, 'dm', finalPayload, {
103
- replyTo: reply_to || null,
104
- });
92
+ const result = await store.sendMessage(myHandle, them, finalMessage || null, 'dm', finalPayload);
105
93
 
106
94
  // Check for errors
107
95
  if (result && result.error) {
@@ -113,6 +101,10 @@ async function handler(args) {
113
101
  // Log social pattern (quietly, in background)
114
102
  patterns.logMessageSent(them);
115
103
 
104
+ // Push event to subscribed agent gateways (Clawdbot, etc.)
105
+ const { pushToAgents } = require('../notify');
106
+ pushToAgents('dm', { from: myHandle, to: them, body: finalMessage }).catch(() => {});
107
+
116
108
  // Record connection in profiles (if first time messaging)
117
109
  try {
118
110
  const hasConnected = await userProfiles.hasBeenConnected(myHandle, them);
@@ -121,7 +113,7 @@ async function handler(args) {
121
113
  }
122
114
  } catch (error) {
123
115
  // Don't fail the message if profile update fails
124
- console.warn('Failed to update profile connection:', error);
116
+ debug('dm', 'Failed to update profile connection:', error);
125
117
  }
126
118
 
127
119
  // Track for session summary
@@ -135,58 +127,27 @@ async function handler(args) {
135
127
  display += ` ${warning(`truncated to ${MAX_LENGTH} chars`)}`;
136
128
  }
137
129
 
138
- // Only show payload type indicator (message already visible in tool call)
130
+ // Show message preview or payload type
131
+ if (finalMessage) {
132
+ display += `\n\n"${truncate(finalMessage, 100)}"`;
133
+ }
139
134
  if (finalPayload) {
140
135
  const payloadType = finalPayload.type || 'data';
141
136
  if (payloadType === 'artifact') {
142
- const icon = finalPayload.template === 'guide' ? '📘' : finalPayload.template === 'learning' ? '💡' : finalPayload.template === 'workspace' ? '🗂️' : '📦';
137
+ const icon =
138
+ finalPayload.template === 'guide'
139
+ ? '📘'
140
+ : finalPayload.template === 'learning'
141
+ ? '💡'
142
+ : finalPayload.template === 'workspace'
143
+ ? '🗂️'
144
+ : '📦';
143
145
  display += `\n\n${icon} _Shared artifact: ${finalPayload.title}_`;
144
146
  } else {
145
147
  display += `\n\n📦 _Includes ${payloadType} payload_`;
146
148
  }
147
149
  }
148
150
 
149
- // Execute attached tip if specified
150
- let tipResult = null;
151
- if (tip_amount_cents && tip_amount_cents > 0) {
152
- const token = config.getToken();
153
- if (token) {
154
- try {
155
- const apiUrl = config.getApiUrl();
156
- // Generate idempotency key to prevent duplicate tips from retries
157
- const timeBucket = Math.floor(Date.now() / 60000);
158
- const tipIdempotencyKey = `dm:${myHandle}:${them}:${tip_amount_cents}:${timeBucket}`;
159
-
160
- const tipResponse = await fetch(`${apiUrl}/api/tips/instant`, {
161
- method: 'POST',
162
- headers: {
163
- 'Content-Type': 'application/json',
164
- 'Authorization': `Bearer ${token}`,
165
- 'Idempotency-Key': tipIdempotencyKey
166
- },
167
- body: JSON.stringify({
168
- to: them,
169
- amount_cents: tip_amount_cents,
170
- message: message ? `${message.substring(0, 50)}...` : null,
171
- context: { type: 'dm_reply' }
172
- })
173
- });
174
-
175
- tipResult = await tipResponse.json();
176
-
177
- if (tipResult.success) {
178
- const tipAmount = (tip_amount_cents / 100).toFixed(0);
179
- display += `\n\n💸 _Tipped $${tipAmount} USDC — [view tx](${tipResult.explorer_url})_`;
180
- } else {
181
- display += `\n\n⚠️ _Tip failed: ${tipResult.message || 'Unknown error'}_`;
182
- }
183
- } catch (tipError) {
184
- console.warn('[dm] Tip execution failed:', tipError.message);
185
- display += `\n\n⚠️ _Tip failed: ${tipError.message}_`;
186
- }
187
- }
188
- }
189
-
190
151
  // Burst notification (5+ messages in one thread)
191
152
  if (burst.triggered && burst.thread === them) {
192
153
  display += `\n\n💬 _${burst.count} messages with @${them} — say "summarize" when done_`;
@@ -214,27 +175,7 @@ async function handler(args) {
214
175
  // Add guided mode actions
215
176
  response.actions = formatActions(actions.afterDm(them));
216
177
 
217
- // Fetch DM suggestions (async, non-blocking for response)
218
- // This adds "You might want to message..." suggestions
219
- try {
220
- const suggestions = await fetchRelevantUsers(myHandle, 'dm_suggest', 3);
221
- if (suggestions && suggestions.matches && suggestions.matches.length > 0) {
222
- // Filter out the person we just messaged
223
- const others = suggestions.matches.filter(m => m.handle !== them);
224
- if (others.length > 0) {
225
- response.dm_suggestions = others.map(m => ({
226
- handle: m.handle,
227
- building: m.building,
228
- reasons: m.reasons?.slice(0, 2) || []
229
- }));
230
- }
231
- }
232
- } catch (e) {
233
- // Don't fail DM if suggestions fail
234
- console.log('[dm] dm_suggest fetch error:', e.message);
235
- }
236
-
237
178
  return response;
238
179
  }
239
180
 
240
- module.exports = { definition, handler };
181
+ module.exports = { definition, handler };
package/tools/doctor.js CHANGED
@@ -13,7 +13,7 @@ const config = require('../config');
13
13
  const store = require('../store');
14
14
  const presence = require('../presence');
15
15
 
16
- const VIBE_API = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
16
+ const VIBE_API = config.getApiUrl();
17
17
  const VIBE_DIR = path.join(process.env.HOME, '.vibe');
18
18
  const MCP_DIR = path.join(VIBE_DIR, 'mcp-server');
19
19
 
@@ -50,10 +50,7 @@ async function diagnoseAPI() {
50
50
  return {
51
51
  status: 'error',
52
52
  message: `API returned HTTP ${res.status}`,
53
- remediation: [
54
- 'Check if slashvibe.dev is up',
55
- 'Try: curl -s https://slashvibe.dev/api/stats'
56
- ]
53
+ remediation: ['Check if slashvibe.dev is up', 'Try: curl -s https://slashvibe.dev/api/stats']
57
54
  };
58
55
  } catch (e) {
59
56
  if (e.name === 'TimeoutError') {
@@ -70,10 +67,7 @@ async function diagnoseAPI() {
70
67
  return {
71
68
  status: 'error',
72
69
  message: `Network error: ${e.message}`,
73
- remediation: [
74
- 'Check your internet connection',
75
- 'Check if VPN is blocking requests'
76
- ]
70
+ remediation: ['Check your internet connection', 'Check if VPN is blocking requests']
77
71
  };
78
72
  }
79
73
  }
@@ -85,9 +79,7 @@ async function diagnoseIdentity(autoFix = false) {
85
79
  return {
86
80
  status: 'error',
87
81
  message: 'Not initialized',
88
- remediation: [
89
- 'Run: vibe init @yourhandle "what you\'re building"'
90
- ],
82
+ remediation: ['Run: vibe init @yourhandle "what you\'re building"'],
91
83
  canAutoFix: false
92
84
  };
93
85
  }
@@ -155,9 +147,7 @@ async function diagnoseIdentity(autoFix = false) {
155
147
  return {
156
148
  status: 'error',
157
149
  message: `Identity check failed: ${e.message}`,
158
- remediation: [
159
- 'Run: vibe init @yourhandle "what you\'re building"'
160
- ]
150
+ remediation: ['Run: vibe init @yourhandle "what you\'re building"']
161
151
  };
162
152
  }
163
153
  }
@@ -180,10 +170,7 @@ async function diagnosePresence() {
180
170
  return {
181
171
  status: 'warning',
182
172
  message: 'Not visible to others',
183
- remediation: [
184
- 'Heartbeat may have stopped',
185
- 'Run: vibe status to trigger heartbeat'
186
- ]
173
+ remediation: ['Heartbeat may have stopped', 'Run: vibe status to trigger heartbeat']
187
174
  };
188
175
  }
189
176
 
@@ -193,10 +180,7 @@ async function diagnosePresence() {
193
180
  return {
194
181
  status: 'warning',
195
182
  message: `Last heartbeat ${minutesAgo}m ago`,
196
- remediation: [
197
- 'Heartbeat may be stale',
198
- 'Run any vibe command to refresh'
199
- ]
183
+ remediation: ['Heartbeat may be stale', 'Run any vibe command to refresh']
200
184
  };
201
185
  }
202
186
 
@@ -272,10 +256,7 @@ async function diagnoseStorage() {
272
256
  return {
273
257
  status: 'error',
274
258
  message: '~/.vibe directory missing',
275
- remediation: [
276
- 'Run: mkdir -p ~/.vibe',
277
- 'Or: vibe init to auto-create'
278
- ],
259
+ remediation: ['Run: mkdir -p ~/.vibe', 'Or: vibe init to auto-create'],
279
260
  canAutoFix: true
280
261
  };
281
262
  }
@@ -302,10 +283,7 @@ async function diagnoseStorage() {
302
283
  return {
303
284
  status: 'warning',
304
285
  message: issues.join(', '),
305
- remediation: [
306
- 'Run: vibe init to recreate session',
307
- 'Memory directory will be created on first use'
308
- ]
286
+ remediation: ['Run: vibe init to recreate session', 'Memory directory will be created on first use']
309
287
  };
310
288
  }
311
289
 
@@ -316,9 +294,7 @@ async function diagnoseStorage() {
316
294
  return {
317
295
  status: 'error',
318
296
  message: 'session.json not readable/writable',
319
- remediation: [
320
- 'Fix permissions: chmod 600 ~/.vibe/session.json'
321
- ]
297
+ remediation: ['Fix permissions: chmod 600 ~/.vibe/session.json']
322
298
  };
323
299
  }
324
300
 
@@ -351,9 +327,7 @@ async function diagnoseMessages() {
351
327
  return {
352
328
  status: 'info',
353
329
  message: `${count} unread message${count > 1 ? 's' : ''}`,
354
- remediation: [
355
- 'Run: vibe inbox to see messages'
356
- ]
330
+ remediation: ['Run: vibe inbox to see messages']
357
331
  };
358
332
  }
359
333
 
@@ -395,10 +369,7 @@ async function diagnoseInstall() {
395
369
  return {
396
370
  status: 'info',
397
371
  message: `v${version} · ${MCP_DIR}`,
398
- remediation: [
399
- `Update: ${updateMethod}`,
400
- 'After update: restart Claude Code'
401
- ]
372
+ remediation: [`Update: ${updateMethod}`, 'After update: restart Claude Code']
402
373
  };
403
374
  } catch (e) {
404
375
  return {
package/tools/drawing.js CHANGED
@@ -7,7 +7,7 @@
7
7
  const config = require('../config');
8
8
  const store = require('../store');
9
9
  const { createGamePayload } = require('../protocol');
10
- const { requireInit, normalizeHandle } = require('./_shared');
10
+ const { requireInit, normalizeHandle, debug } = require('./_shared');
11
11
 
12
12
  // Drawing game implementation
13
13
  const drawing = require('../games/drawing');
@@ -32,7 +32,7 @@ const definition = {
32
32
  description: 'X coordinate (0-19) for drawing'
33
33
  },
34
34
  y: {
35
- type: 'number',
35
+ type: 'number',
36
36
  description: 'Y coordinate (0-11) for drawing'
37
37
  },
38
38
  x1: {
@@ -45,7 +45,8 @@ const definition = {
45
45
  },
46
46
  char: {
47
47
  type: 'string',
48
- description: 'Character to draw (empty, dot, circle, square, star, heart, tree, house, sun, moon, water, mountain, person, cat, dog, car, plane, flower, umbrella, rainbow)'
48
+ description:
49
+ 'Character to draw (empty, dot, circle, square, star, heart, tree, house, sun, moon, water, mountain, person, cat, dog, car, plane, flower, umbrella, rainbow)'
49
50
  },
50
51
  theme: {
51
52
  type: 'string',
@@ -65,7 +66,7 @@ async function getDrawingState(room) {
65
66
  const state = await store.get(key);
66
67
  return state ? JSON.parse(state) : null;
67
68
  } catch (e) {
68
- console.error('[drawing] Failed to get state:', e.message);
69
+ debug('drawing', 'Failed to get state:', e.message);
69
70
  return null;
70
71
  }
71
72
  }
@@ -78,7 +79,7 @@ async function saveDrawingState(room, state) {
78
79
  const key = `drawing:${room}`;
79
80
  await store.set(key, JSON.stringify(state));
80
81
  } catch (e) {
81
- console.error('[drawing] Failed to save state:', e.message);
82
+ debug('drawing', 'Failed to save state:', e.message);
82
83
  }
83
84
  }
84
85
 
@@ -86,7 +87,7 @@ async function saveDrawingState(room, state) {
86
87
  * Post drawing activity to board
87
88
  */
88
89
  async function postDrawingActivity(action, room, player, details = '') {
89
- const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
90
+ const API_URL = config.getApiUrl();
90
91
 
91
92
  try {
92
93
  let content;
@@ -114,7 +115,7 @@ async function postDrawingActivity(action, room, player, details = '') {
114
115
  })
115
116
  });
116
117
  } catch (e) {
117
- console.error('[drawing] Failed to post to board:', e.message);
118
+ debug('drawing', 'Failed to post to board:', e.message);
118
119
  }
119
120
  }
120
121
 
@@ -146,7 +147,9 @@ async function handler(args) {
146
147
 
147
148
  case 'join':
148
149
  if (!drawingState) {
149
- return { display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!` };
150
+ return {
151
+ display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!`
152
+ };
150
153
  }
151
154
 
152
155
  const joinResult = drawing.addPlayer(drawingState, myHandle);
@@ -164,7 +167,9 @@ async function handler(args) {
164
167
 
165
168
  case 'draw':
166
169
  if (!drawingState) {
167
- return { display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!` };
170
+ return {
171
+ display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!`
172
+ };
168
173
  }
169
174
 
170
175
  if (x === undefined || y === undefined || !char) {
@@ -192,11 +197,16 @@ async function handler(args) {
192
197
 
193
198
  case 'line':
194
199
  if (!drawingState) {
195
- return { display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!` };
200
+ return {
201
+ display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!`
202
+ };
196
203
  }
197
204
 
198
205
  if (x === undefined || y === undefined || x1 === undefined || y1 === undefined || !char) {
199
- return { display: 'For line drawing, you need: --x [start x] --y [start y] --x1 [end x] --y1 [end y] --char [character name]' };
206
+ return {
207
+ display:
208
+ 'For line drawing, you need: --x [start x] --y [start y] --x1 [end x] --y1 [end y] --char [character name]'
209
+ };
200
210
  }
201
211
 
202
212
  const charName2 = char.toLowerCase();
@@ -222,10 +232,12 @@ async function handler(args) {
222
232
  return { display: `No drawing session found in #${room}.` };
223
233
  }
224
234
 
225
- const clearResult = drawing.clearRegion(drawingState,
226
- x || 0, y || 0,
227
- x1 !== undefined ? x1 : drawing.CANVAS_WIDTH - 1,
228
- y1 !== undefined ? y1 : drawing.CANVAS_HEIGHT - 1,
235
+ const clearResult = drawing.clearRegion(
236
+ drawingState,
237
+ x || 0,
238
+ y || 0,
239
+ x1 !== undefined ? x1 : drawing.CANVAS_WIDTH - 1,
240
+ y1 !== undefined ? y1 : drawing.CANVAS_HEIGHT - 1,
229
241
  myHandle
230
242
  );
231
243
 
@@ -259,14 +271,16 @@ async function handler(args) {
259
271
  await postDrawingActivity('theme', room, myHandle, theme);
260
272
 
261
273
  const tips = drawing.getDrawingTips(theme);
262
-
274
+
263
275
  return {
264
276
  display: `## 🎯 Set Theme in #${room}\n\n${drawing.formatDrawingDisplay(drawingState)}\n\n**Drawing tips for "${theme}":**\n${tips.map(tip => `• ${tip}`).join('\n')}`
265
277
  };
266
278
 
267
279
  case 'view':
268
280
  if (!drawingState) {
269
- return { display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!` };
281
+ return {
282
+ display: `No drawing session found in #${room}. Use \`vibe drawing --action start --room ${room}\` to create one!`
283
+ };
270
284
  }
271
285
 
272
286
  return {
@@ -279,7 +293,7 @@ async function handler(args) {
279
293
  }
280
294
 
281
295
  const stats = drawing.getCanvasStats(drawingState);
282
-
296
+
283
297
  let statsDisplay = `## 📊 Drawing Stats for #${room}\n\n`;
284
298
  statsDisplay += `**Canvas:** ${stats.fillPercentage}% filled (${stats.totalDrawnCells}/${drawing.CANVAS_WIDTH * drawing.CANVAS_HEIGHT} cells)\n`;
285
299
  statsDisplay += `**Moves:** ${stats.totalMoves} total\n`;
@@ -292,7 +306,7 @@ async function handler(args) {
292
306
  if (Object.keys(stats.playerMoves).length > 0) {
293
307
  statsDisplay += `**Artists:**\n`;
294
308
  Object.entries(stats.playerMoves)
295
- .sort(([,a], [,b]) => b - a)
309
+ .sort(([, a], [, b]) => b - a)
296
310
  .forEach(([player, moves]) => {
297
311
  statsDisplay += `• @${player}: ${moves} moves\n`;
298
312
  });
@@ -307,4 +321,4 @@ async function handler(args) {
307
321
  }
308
322
  }
309
323
 
310
- module.exports = { definition, handler };
324
+ module.exports = { definition, handler };
package/tools/echo.js CHANGED
@@ -14,7 +14,7 @@ const config = require('../config');
14
14
  const { formatTimeAgo, requireInit } = require('./_shared');
15
15
  const store = require('../store');
16
16
 
17
- const API_URL = process.env.VIBE_API_URL || 'https://www.slashvibe.dev';
17
+ const API_URL = config.getApiUrl();
18
18
 
19
19
  const definition = {
20
20
  name: 'vibe_echo',
@@ -53,20 +53,20 @@ function isQuery(message) {
53
53
  // @echo personality responses
54
54
  const responses = {
55
55
  received: [
56
- "Got it! 📝 Feedback received and shared with everyone.",
57
- "Noted! 📝 Added to the feedback stream.",
58
- "Heard you loud and clear! 🎧",
59
- "Thanks for speaking up! 📣 Your feedback is now visible to all."
56
+ 'Got it! 📝 Feedback received and shared with everyone.',
57
+ 'Noted! 📝 Added to the feedback stream.',
58
+ 'Heard you loud and clear! 🎧',
59
+ 'Thanks for speaking up! 📣 Your feedback is now visible to all.'
60
60
  ],
61
61
  receivedAnon: [
62
- "🔒 Stored anonymously. Thanks for helping make /vibe better!",
63
- "🔒 Anonymous feedback saved. Your voice matters!",
64
- "🔒 Noted anonymously. Appreciate you!"
62
+ '🔒 Stored anonymously. Thanks for helping make /vibe better!',
63
+ '🔒 Anonymous feedback saved. Your voice matters!',
64
+ '🔒 Noted anonymously. Appreciate you!'
65
65
  ],
66
66
  empty: [
67
- "No feedback yet. Be the first to share! 🎤",
68
- "The feedback stream is empty... for now. 🔇",
69
- "Crickets. Share your thoughts to get things started! 🦗"
67
+ 'No feedback yet. Be the first to share! 🎤',
68
+ 'The feedback stream is empty... for now. 🔇',
69
+ 'Crickets. Share your thoughts to get things started! 🦗'
70
70
  ],
71
71
  greeting: [
72
72
  "Hey! I'm @echo, the /vibe feedback agent. 🎧",