slashvibe-mcp 0.2.2 → 0.2.3

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +58 -40
  3. package/config.js +171 -3
  4. package/index.js +136 -16
  5. package/intelligence/index.js +38 -0
  6. package/intelligence/infer.js +316 -0
  7. package/intelligence/patterns.js +651 -0
  8. package/intelligence/proactive.js +358 -0
  9. package/intelligence/serendipity.js +306 -0
  10. package/notify.js +141 -18
  11. package/package.json +8 -4
  12. package/presence.js +5 -1
  13. package/protocol/index.js +88 -1
  14. package/protocol/telegram-commands.js +199 -0
  15. package/store/api.js +360 -25
  16. package/store/index.js +7 -7
  17. package/store/local.js +67 -11
  18. package/store/profiles.js +287 -0
  19. package/store/reservations.js +321 -0
  20. package/store/skills.js +378 -0
  21. package/tools/_actions.js +270 -14
  22. package/tools/_connection-queue.js +257 -0
  23. package/tools/_discovery-enhanced.js +290 -0
  24. package/tools/_discovery.js +346 -0
  25. package/tools/_proactive-discovery.js +301 -0
  26. package/tools/admin-inbox.js +218 -0
  27. package/tools/agent-treasury.js +288 -0
  28. package/tools/agents.js +122 -0
  29. package/tools/arcade.js +173 -0
  30. package/tools/artifact-create.js +236 -0
  31. package/tools/artifact-view.js +174 -0
  32. package/tools/ask-expert.js +160 -0
  33. package/tools/auto-suggest-connections.js +304 -0
  34. package/tools/away.js +68 -0
  35. package/tools/back.js +51 -0
  36. package/tools/become-expert.js +150 -0
  37. package/tools/bootstrap-skills.js +231 -0
  38. package/tools/bridge-dashboard.js +342 -0
  39. package/tools/bridge-health.js +400 -0
  40. package/tools/bridge-live.js +384 -0
  41. package/tools/bridges.js +383 -0
  42. package/tools/bye.js +4 -0
  43. package/tools/collaborative-drawing.js +286 -0
  44. package/tools/colorguess.js +281 -0
  45. package/tools/crossword.js +369 -0
  46. package/tools/discover-insights.js +379 -0
  47. package/tools/discover-momentum.js +256 -0
  48. package/tools/discover.js +395 -0
  49. package/tools/discovery-analytics.js +345 -0
  50. package/tools/discovery-auto-suggest.js +275 -0
  51. package/tools/discovery-bootstrap.js +267 -0
  52. package/tools/discovery-daily.js +375 -0
  53. package/tools/discovery-dashboard.js +385 -0
  54. package/tools/discovery-digest.js +314 -0
  55. package/tools/discovery-hub.js +357 -0
  56. package/tools/discovery-insights.js +384 -0
  57. package/tools/discovery-momentum.js +281 -0
  58. package/tools/discovery-monitor.js +319 -0
  59. package/tools/discovery-proactive.js +300 -0
  60. package/tools/dm.js +62 -9
  61. package/tools/draw.js +317 -0
  62. package/tools/drawing.js +310 -0
  63. package/tools/echo.js +16 -0
  64. package/tools/farcaster.js +307 -0
  65. package/tools/feed.js +196 -0
  66. package/tools/game.js +218 -110
  67. package/tools/games-catalog.js +376 -0
  68. package/tools/games.js +313 -0
  69. package/tools/genesis.js +233 -0
  70. package/tools/guessnumber.js +194 -0
  71. package/tools/hangman.js +129 -0
  72. package/tools/help.js +269 -0
  73. package/tools/idea.js +210 -0
  74. package/tools/inbox.js +148 -25
  75. package/tools/init.js +651 -33
  76. package/tools/insights.js +123 -0
  77. package/tools/invite.js +142 -21
  78. package/tools/l2-bridge.js +272 -0
  79. package/tools/l2-status.js +217 -0
  80. package/tools/l2.js +206 -0
  81. package/tools/migrate.js +156 -0
  82. package/tools/mint.js +377 -0
  83. package/tools/multiplayer-game.js +275 -0
  84. package/tools/multiplayer-tictactoe.js +303 -0
  85. package/tools/mute.js +97 -0
  86. package/tools/notifications.js +415 -0
  87. package/tools/observe.js +200 -0
  88. package/tools/onboarding.js +147 -0
  89. package/tools/open.js +14 -2
  90. package/tools/party-game.js +314 -0
  91. package/tools/presence-agent.js +167 -0
  92. package/tools/profile.js +219 -0
  93. package/tools/pulse.js +218 -0
  94. package/tools/react.js +4 -0
  95. package/tools/release.js +83 -0
  96. package/tools/report.js +109 -0
  97. package/tools/reputation.js +175 -0
  98. package/tools/request.js +217 -0
  99. package/tools/reservations.js +116 -0
  100. package/tools/reserve.js +111 -0
  101. package/tools/riddle.js +240 -0
  102. package/tools/run-bootstrap.js +69 -0
  103. package/tools/settings.js +112 -0
  104. package/tools/ship.js +182 -0
  105. package/tools/shipback.js +326 -0
  106. package/tools/skills-analytics.js +349 -0
  107. package/tools/skills-bootstrap.js +301 -0
  108. package/tools/skills-dashboard.js +268 -0
  109. package/tools/skills-exchange.js +342 -0
  110. package/tools/skills.js +380 -0
  111. package/tools/smart-intro.js +353 -0
  112. package/tools/social-inbox.js +326 -69
  113. package/tools/social-post.js +251 -66
  114. package/tools/social-processor.js +445 -0
  115. package/tools/solo-game.js +390 -0
  116. package/tools/start.js +205 -83
  117. package/tools/storybuilder.js +331 -0
  118. package/tools/suggest-tags.js +186 -0
  119. package/tools/tag-suggestions.js +257 -0
  120. package/tools/telegram-bot.js +183 -0
  121. package/tools/telegram-setup.js +214 -0
  122. package/tools/tictactoe.js +155 -0
  123. package/tools/tip.js +120 -0
  124. package/tools/token.js +103 -0
  125. package/tools/twentyquestions.js +143 -0
  126. package/tools/wallet.js +127 -0
  127. package/tools/webhook-test.js +388 -0
  128. package/tools/who.js +118 -25
  129. package/tools/wordassociation.js +247 -0
  130. package/tools/workshop-buddy.js +394 -0
  131. package/tools/workshop.js +327 -0
  132. package/version.json +12 -3
  133. package/tools/board.js +0 -130
@@ -0,0 +1,651 @@
1
+ /**
2
+ * Work Patterns — Ambient behavioral memory
3
+ *
4
+ * Quietly observes and remembers:
5
+ * - Session timing (when you work, how long)
6
+ * - State patterns (how often in each state)
7
+ * - Module affinity (where you spend time)
8
+ *
9
+ * Stored locally in ~/.vibe/work-patterns.json
10
+ * Never transmitted. Private by default.
11
+ */
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const PATTERNS_FILE = path.join(process.env.HOME, '.vibe', 'work-patterns.json');
17
+
18
+ // Ensure directory exists
19
+ function ensureDir() {
20
+ const dir = path.dirname(PATTERNS_FILE);
21
+ if (!fs.existsSync(dir)) {
22
+ fs.mkdirSync(dir, { recursive: true });
23
+ }
24
+ }
25
+
26
+ // Load patterns from disk
27
+ function load() {
28
+ try {
29
+ if (fs.existsSync(PATTERNS_FILE)) {
30
+ return JSON.parse(fs.readFileSync(PATTERNS_FILE, 'utf8'));
31
+ }
32
+ } catch (e) {
33
+ console.error('[patterns] Load error:', e.message);
34
+ }
35
+ return createEmpty();
36
+ }
37
+
38
+ // Save patterns to disk
39
+ function save(patterns) {
40
+ ensureDir();
41
+ try {
42
+ fs.writeFileSync(PATTERNS_FILE, JSON.stringify(patterns, null, 2));
43
+ } catch (e) {
44
+ console.error('[patterns] Save error:', e.message);
45
+ }
46
+ }
47
+
48
+ // Create empty patterns structure
49
+ function createEmpty() {
50
+ return {
51
+ version: 2,
52
+ firstSeen: new Date().toISOString(),
53
+ lastUpdated: new Date().toISOString(),
54
+
55
+ // === WORK PATTERNS ===
56
+ sessions: {
57
+ total: 0,
58
+ totalMinutes: 0,
59
+ byHour: Array(24).fill(0), // Activity by hour of day
60
+ byDay: Array(7).fill(0), // Activity by day of week (0=Sun)
61
+ longestMinutes: 0,
62
+ averageMinutes: 0
63
+ },
64
+
65
+ states: {
66
+ // state -> { count, totalMinutes }
67
+ },
68
+
69
+ modules: {
70
+ // module -> { visits, totalMinutes }
71
+ },
72
+
73
+ recentSessions: [],
74
+
75
+ // === SOCIAL PATTERNS ===
76
+ social: {
77
+ // handle -> { messages, lastContact, topics: [] }
78
+ connections: {},
79
+ // Total interaction counts
80
+ messagesSent: 0,
81
+ messagesReceived: 0,
82
+ reactionsGiven: 0,
83
+ reactionsReceived: 0,
84
+ // Who they interact with most
85
+ topConnections: []
86
+ },
87
+
88
+ // === CREATIVE PATTERNS ===
89
+ creative: {
90
+ // What they've shipped
91
+ ships: [],
92
+ // Ideas they've posted
93
+ ideas: [],
94
+ // Ideas they've riffed on
95
+ riffs: [],
96
+ // Domains they explore
97
+ domains: {},
98
+ // Attribution given/received
99
+ inspired: [], // who inspired them
100
+ inspiredOthers: [] // who they've inspired
101
+ }
102
+ };
103
+ }
104
+
105
+ // ============ EVENT LOGGING ============
106
+
107
+ /**
108
+ * Log session start
109
+ */
110
+ function logSessionStart(handle) {
111
+ const patterns = load();
112
+ const now = new Date();
113
+
114
+ patterns.sessions.total++;
115
+ patterns.sessions.byHour[now.getHours()]++;
116
+ patterns.sessions.byDay[now.getDay()]++;
117
+
118
+ // Track current session
119
+ patterns._currentSession = {
120
+ start: now.toISOString(),
121
+ handle,
122
+ states: [],
123
+ modules: []
124
+ };
125
+
126
+ patterns.lastUpdated = now.toISOString();
127
+ save(patterns);
128
+ }
129
+
130
+ /**
131
+ * Log session end
132
+ */
133
+ function logSessionEnd() {
134
+ const patterns = load();
135
+ if (!patterns._currentSession) return;
136
+
137
+ const now = new Date();
138
+ const start = new Date(patterns._currentSession.start);
139
+ const durationMinutes = Math.round((now - start) / 60000);
140
+
141
+ // Update totals
142
+ patterns.sessions.totalMinutes += durationMinutes;
143
+ patterns.sessions.averageMinutes = Math.round(
144
+ patterns.sessions.totalMinutes / patterns.sessions.total
145
+ );
146
+ if (durationMinutes > patterns.sessions.longestMinutes) {
147
+ patterns.sessions.longestMinutes = durationMinutes;
148
+ }
149
+
150
+ // Add to recent sessions
151
+ patterns.recentSessions.unshift({
152
+ date: patterns._currentSession.start,
153
+ durationMinutes,
154
+ states: patterns._currentSession.states,
155
+ modules: patterns._currentSession.modules
156
+ });
157
+
158
+ // Keep only last 20 sessions
159
+ if (patterns.recentSessions.length > 20) {
160
+ patterns.recentSessions = patterns.recentSessions.slice(0, 20);
161
+ }
162
+
163
+ delete patterns._currentSession;
164
+ patterns.lastUpdated = now.toISOString();
165
+ save(patterns);
166
+ }
167
+
168
+ /**
169
+ * Log state observation
170
+ */
171
+ function logState(state, durationMinutes = 5) {
172
+ if (!state) return;
173
+
174
+ const patterns = load();
175
+
176
+ // Initialize state if new
177
+ if (!patterns.states[state]) {
178
+ patterns.states[state] = { count: 0, totalMinutes: 0 };
179
+ }
180
+
181
+ patterns.states[state].count++;
182
+ patterns.states[state].totalMinutes += durationMinutes;
183
+
184
+ // Track in current session
185
+ if (patterns._currentSession) {
186
+ if (!patterns._currentSession.states.includes(state)) {
187
+ patterns._currentSession.states.push(state);
188
+ }
189
+ }
190
+
191
+ patterns.lastUpdated = new Date().toISOString();
192
+ save(patterns);
193
+ }
194
+
195
+ /**
196
+ * Log module/file observation
197
+ */
198
+ function logModule(filePath) {
199
+ if (!filePath) return;
200
+
201
+ const patterns = load();
202
+
203
+ // Extract module from path
204
+ const module = extractModule(filePath);
205
+ if (!module) return;
206
+
207
+ // Initialize module if new
208
+ if (!patterns.modules[module]) {
209
+ patterns.modules[module] = { visits: 0, totalMinutes: 0 };
210
+ }
211
+
212
+ patterns.modules[module].visits++;
213
+ patterns.modules[module].totalMinutes += 5; // Assume 5min per observation
214
+
215
+ // Track in current session
216
+ if (patterns._currentSession) {
217
+ if (!patterns._currentSession.modules.includes(module)) {
218
+ patterns._currentSession.modules.push(module);
219
+ }
220
+ }
221
+
222
+ patterns.lastUpdated = new Date().toISOString();
223
+ save(patterns);
224
+ }
225
+
226
+ // ============ SOCIAL LOGGING ============
227
+
228
+ /**
229
+ * Log a message sent to someone
230
+ */
231
+ function logMessageSent(toHandle, topic = null) {
232
+ if (!toHandle) return;
233
+
234
+ const patterns = load();
235
+ const handle = toHandle.replace('@', '').toLowerCase();
236
+
237
+ // Initialize connection if new
238
+ if (!patterns.social.connections[handle]) {
239
+ patterns.social.connections[handle] = {
240
+ messages: 0,
241
+ received: 0,
242
+ lastContact: null,
243
+ topics: []
244
+ };
245
+ }
246
+
247
+ patterns.social.connections[handle].messages++;
248
+ patterns.social.connections[handle].lastContact = new Date().toISOString();
249
+ patterns.social.messagesSent++;
250
+
251
+ // Track topic if provided
252
+ if (topic && !patterns.social.connections[handle].topics.includes(topic)) {
253
+ patterns.social.connections[handle].topics.push(topic);
254
+ // Keep topics list reasonable
255
+ if (patterns.social.connections[handle].topics.length > 10) {
256
+ patterns.social.connections[handle].topics.shift();
257
+ }
258
+ }
259
+
260
+ updateTopConnections(patterns);
261
+ patterns.lastUpdated = new Date().toISOString();
262
+ save(patterns);
263
+ }
264
+
265
+ /**
266
+ * Log a message received from someone
267
+ */
268
+ function logMessageReceived(fromHandle) {
269
+ if (!fromHandle) return;
270
+
271
+ const patterns = load();
272
+ const handle = fromHandle.replace('@', '').toLowerCase();
273
+
274
+ if (!patterns.social.connections[handle]) {
275
+ patterns.social.connections[handle] = {
276
+ messages: 0,
277
+ received: 0,
278
+ lastContact: null,
279
+ topics: []
280
+ };
281
+ }
282
+
283
+ patterns.social.connections[handle].received++;
284
+ patterns.social.connections[handle].lastContact = new Date().toISOString();
285
+ patterns.social.messagesReceived++;
286
+
287
+ updateTopConnections(patterns);
288
+ patterns.lastUpdated = new Date().toISOString();
289
+ save(patterns);
290
+ }
291
+
292
+ /**
293
+ * Log a reaction given
294
+ */
295
+ function logReaction(toHandle, reaction) {
296
+ if (!toHandle) return;
297
+
298
+ const patterns = load();
299
+ patterns.social.reactionsGiven++;
300
+ patterns.lastUpdated = new Date().toISOString();
301
+ save(patterns);
302
+ }
303
+
304
+ /**
305
+ * Update top connections list (internal)
306
+ */
307
+ function updateTopConnections(patterns) {
308
+ const connections = Object.entries(patterns.social.connections);
309
+ connections.sort((a, b) => {
310
+ const aTotal = a[1].messages + a[1].received;
311
+ const bTotal = b[1].messages + b[1].received;
312
+ return bTotal - aTotal;
313
+ });
314
+
315
+ patterns.social.topConnections = connections
316
+ .slice(0, 5)
317
+ .map(([handle, data]) => ({
318
+ handle,
319
+ total: data.messages + data.received,
320
+ lastContact: data.lastContact
321
+ }));
322
+ }
323
+
324
+ // ============ CREATIVE LOGGING ============
325
+
326
+ /**
327
+ * Log something shipped
328
+ */
329
+ function logShip(what, url = null, tags = []) {
330
+ const patterns = load();
331
+
332
+ patterns.creative.ships.unshift({
333
+ what,
334
+ url,
335
+ tags,
336
+ timestamp: new Date().toISOString()
337
+ });
338
+
339
+ // Keep last 20 ships
340
+ if (patterns.creative.ships.length > 20) {
341
+ patterns.creative.ships = patterns.creative.ships.slice(0, 20);
342
+ }
343
+
344
+ // Track domains from tags
345
+ tags.forEach(tag => {
346
+ if (!patterns.creative.domains[tag]) {
347
+ patterns.creative.domains[tag] = 0;
348
+ }
349
+ patterns.creative.domains[tag]++;
350
+ });
351
+
352
+ patterns.lastUpdated = new Date().toISOString();
353
+ save(patterns);
354
+ }
355
+
356
+ /**
357
+ * Log an idea posted
358
+ */
359
+ function logIdea(content, tags = []) {
360
+ const patterns = load();
361
+
362
+ patterns.creative.ideas.unshift({
363
+ content: content.substring(0, 100),
364
+ tags,
365
+ timestamp: new Date().toISOString()
366
+ });
367
+
368
+ // Keep last 20
369
+ if (patterns.creative.ideas.length > 20) {
370
+ patterns.creative.ideas = patterns.creative.ideas.slice(0, 20);
371
+ }
372
+
373
+ patterns.lastUpdated = new Date().toISOString();
374
+ save(patterns);
375
+ }
376
+
377
+ /**
378
+ * Log a riff on someone's idea
379
+ */
380
+ function logRiff(originalAuthor, content) {
381
+ const patterns = load();
382
+
383
+ patterns.creative.riffs.unshift({
384
+ on: originalAuthor.replace('@', '').toLowerCase(),
385
+ content: content.substring(0, 100),
386
+ timestamp: new Date().toISOString()
387
+ });
388
+
389
+ // Keep last 20
390
+ if (patterns.creative.riffs.length > 20) {
391
+ patterns.creative.riffs = patterns.creative.riffs.slice(0, 20);
392
+ }
393
+
394
+ patterns.lastUpdated = new Date().toISOString();
395
+ save(patterns);
396
+ }
397
+
398
+ /**
399
+ * Log attribution (inspired by someone)
400
+ */
401
+ function logInspiredBy(handle) {
402
+ if (!handle) return;
403
+
404
+ const patterns = load();
405
+ const clean = handle.replace('@', '').toLowerCase();
406
+
407
+ // Track who inspires them
408
+ const existing = patterns.creative.inspired.find(i => i.handle === clean);
409
+ if (existing) {
410
+ existing.count++;
411
+ existing.lastTime = new Date().toISOString();
412
+ } else {
413
+ patterns.creative.inspired.push({
414
+ handle: clean,
415
+ count: 1,
416
+ lastTime: new Date().toISOString()
417
+ });
418
+ }
419
+
420
+ patterns.lastUpdated = new Date().toISOString();
421
+ save(patterns);
422
+ }
423
+
424
+ // ============ PATTERN QUERIES ============
425
+
426
+ /**
427
+ * Get peak hours (when user is most active)
428
+ */
429
+ function getPeakHours() {
430
+ const patterns = load();
431
+ const hours = patterns.sessions.byHour;
432
+
433
+ // Find top 3 hours
434
+ const indexed = hours.map((count, hour) => ({ hour, count }));
435
+ indexed.sort((a, b) => b.count - a.count);
436
+
437
+ return indexed.slice(0, 3).filter(h => h.count > 0);
438
+ }
439
+
440
+ /**
441
+ * Get dominant state (most common)
442
+ */
443
+ function getDominantState() {
444
+ const patterns = load();
445
+ const states = Object.entries(patterns.states);
446
+
447
+ if (states.length === 0) return null;
448
+
449
+ states.sort((a, b) => b[1].totalMinutes - a[1].totalMinutes);
450
+ return {
451
+ state: states[0][0],
452
+ minutes: states[0][1].totalMinutes,
453
+ percentage: Math.round(
454
+ (states[0][1].totalMinutes / patterns.sessions.totalMinutes) * 100
455
+ )
456
+ };
457
+ }
458
+
459
+ /**
460
+ * Get top modules (where user spends most time)
461
+ */
462
+ function getTopModules(limit = 3) {
463
+ const patterns = load();
464
+ const modules = Object.entries(patterns.modules);
465
+
466
+ if (modules.length === 0) return [];
467
+
468
+ modules.sort((a, b) => b[1].totalMinutes - a[1].totalMinutes);
469
+ return modules.slice(0, limit).map(([name, data]) => ({
470
+ name,
471
+ minutes: data.totalMinutes,
472
+ visits: data.visits
473
+ }));
474
+ }
475
+
476
+ /**
477
+ * Get session rhythm (typical session length, frequency)
478
+ */
479
+ function getSessionRhythm() {
480
+ const patterns = load();
481
+
482
+ return {
483
+ totalSessions: patterns.sessions.total,
484
+ averageMinutes: patterns.sessions.averageMinutes,
485
+ longestMinutes: patterns.sessions.longestMinutes,
486
+ totalHours: Math.round(patterns.sessions.totalMinutes / 60)
487
+ };
488
+ }
489
+
490
+ /**
491
+ * Get full patterns summary (for debugging/display)
492
+ */
493
+ function getSummary() {
494
+ const patterns = load();
495
+
496
+ return {
497
+ since: patterns.firstSeen,
498
+ sessions: getSessionRhythm(),
499
+ peakHours: getPeakHours(),
500
+ dominantState: getDominantState(),
501
+ topModules: getTopModules(),
502
+ recentSessions: patterns.recentSessions.slice(0, 5)
503
+ };
504
+ }
505
+
506
+ /**
507
+ * Check if we have enough data for insights
508
+ */
509
+ function hasEnoughData() {
510
+ const patterns = load();
511
+ return patterns.sessions.total >= 3;
512
+ }
513
+
514
+ // ============ SOCIAL QUERIES ============
515
+
516
+ /**
517
+ * Get top connections (people they interact with most)
518
+ */
519
+ function getTopConnections(limit = 5) {
520
+ const patterns = load();
521
+ return patterns.social.topConnections.slice(0, limit);
522
+ }
523
+
524
+ /**
525
+ * Get connection details for a specific handle
526
+ */
527
+ function getConnection(handle) {
528
+ const patterns = load();
529
+ const clean = handle.replace('@', '').toLowerCase();
530
+ return patterns.social.connections[clean] || null;
531
+ }
532
+
533
+ /**
534
+ * Get social summary
535
+ */
536
+ function getSocialSummary() {
537
+ const patterns = load();
538
+ return {
539
+ messagesSent: patterns.social.messagesSent,
540
+ messagesReceived: patterns.social.messagesReceived,
541
+ reactionsGiven: patterns.social.reactionsGiven,
542
+ uniqueConnections: Object.keys(patterns.social.connections).length,
543
+ topConnections: patterns.social.topConnections
544
+ };
545
+ }
546
+
547
+ // ============ CREATIVE QUERIES ============
548
+
549
+ /**
550
+ * Get recent ships
551
+ */
552
+ function getRecentShips(limit = 5) {
553
+ const patterns = load();
554
+ return patterns.creative.ships.slice(0, limit);
555
+ }
556
+
557
+ /**
558
+ * Get top creative domains (what they build)
559
+ */
560
+ function getTopDomains(limit = 5) {
561
+ const patterns = load();
562
+ const domains = Object.entries(patterns.creative.domains);
563
+ domains.sort((a, b) => b[1] - a[1]);
564
+ return domains.slice(0, limit).map(([domain, count]) => ({ domain, count }));
565
+ }
566
+
567
+ /**
568
+ * Get creative inspirations (who inspires them)
569
+ */
570
+ function getInspirations(limit = 5) {
571
+ const patterns = load();
572
+ const inspired = [...patterns.creative.inspired];
573
+ inspired.sort((a, b) => b.count - a.count);
574
+ return inspired.slice(0, limit);
575
+ }
576
+
577
+ /**
578
+ * Get creative summary
579
+ */
580
+ function getCreativeSummary() {
581
+ const patterns = load();
582
+ return {
583
+ totalShips: patterns.creative.ships.length,
584
+ totalIdeas: patterns.creative.ideas.length,
585
+ totalRiffs: patterns.creative.riffs.length,
586
+ topDomains: getTopDomains(3),
587
+ inspirations: getInspirations(3),
588
+ recentShips: patterns.creative.ships.slice(0, 3)
589
+ };
590
+ }
591
+
592
+ // ============ HELPERS ============
593
+
594
+ function extractModule(filePath) {
595
+ if (!filePath) return null;
596
+
597
+ const parts = filePath.split('/');
598
+ const meaningful = ['src', 'lib', 'app', 'components', 'pages', 'api',
599
+ 'services', 'utils', 'hooks', 'store', 'models'];
600
+
601
+ for (let i = 0; i < parts.length - 1; i++) {
602
+ if (meaningful.includes(parts[i])) {
603
+ return parts[i + 1] || parts[i];
604
+ }
605
+ }
606
+
607
+ // Default to parent directory
608
+ return parts.length >= 2 ? parts[parts.length - 2] : null;
609
+ }
610
+
611
+ module.exports = {
612
+ // Work logging
613
+ logSessionStart,
614
+ logSessionEnd,
615
+ logState,
616
+ logModule,
617
+
618
+ // Social logging
619
+ logMessageSent,
620
+ logMessageReceived,
621
+ logReaction,
622
+
623
+ // Creative logging
624
+ logShip,
625
+ logIdea,
626
+ logRiff,
627
+ logInspiredBy,
628
+
629
+ // Work queries
630
+ getPeakHours,
631
+ getDominantState,
632
+ getTopModules,
633
+ getSessionRhythm,
634
+ getSummary,
635
+ hasEnoughData,
636
+
637
+ // Social queries
638
+ getTopConnections,
639
+ getConnection,
640
+ getSocialSummary,
641
+
642
+ // Creative queries
643
+ getRecentShips,
644
+ getTopDomains,
645
+ getInspirations,
646
+ getCreativeSummary,
647
+
648
+ // Direct access
649
+ load,
650
+ save
651
+ };