@equilateral_ai/mindmeld 3.0.0

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 (86) hide show
  1. package/README.md +300 -0
  2. package/hooks/README.md +494 -0
  3. package/hooks/pre-compact.js +392 -0
  4. package/hooks/session-start.js +264 -0
  5. package/package.json +90 -0
  6. package/scripts/harvest.js +561 -0
  7. package/scripts/init-project.js +437 -0
  8. package/scripts/inject.js +388 -0
  9. package/src/collaboration/CollaborationPrompt.js +460 -0
  10. package/src/core/AlertEngine.js +813 -0
  11. package/src/core/AlertNotifier.js +363 -0
  12. package/src/core/CorrelationAnalyzer.js +774 -0
  13. package/src/core/CurationEngine.js +688 -0
  14. package/src/core/LLMPatternDetector.js +508 -0
  15. package/src/core/LoadBearingDetector.js +242 -0
  16. package/src/core/NotificationService.js +1032 -0
  17. package/src/core/PatternValidator.js +355 -0
  18. package/src/core/README.md +160 -0
  19. package/src/core/RapportOrchestrator.js +446 -0
  20. package/src/core/RelevanceDetector.js +577 -0
  21. package/src/core/StandardsIngestion.js +575 -0
  22. package/src/core/TeamLoadBearingDetector.js +431 -0
  23. package/src/database/dbOperations.js +105 -0
  24. package/src/handlers/activity/activityGetMe.js +98 -0
  25. package/src/handlers/activity/activityGetTeam.js +130 -0
  26. package/src/handlers/alerts/alertsAcknowledge.js +91 -0
  27. package/src/handlers/alerts/alertsGet.js +250 -0
  28. package/src/handlers/collaborators/collaboratorAdd.js +201 -0
  29. package/src/handlers/collaborators/collaboratorInvite.js +218 -0
  30. package/src/handlers/collaborators/collaboratorList.js +88 -0
  31. package/src/handlers/collaborators/collaboratorRemove.js +127 -0
  32. package/src/handlers/collaborators/inviteAccept.js +122 -0
  33. package/src/handlers/context/contextGet.js +57 -0
  34. package/src/handlers/context/invariantsGet.js +74 -0
  35. package/src/handlers/context/loopsGet.js +82 -0
  36. package/src/handlers/context/notesCreate.js +74 -0
  37. package/src/handlers/context/purposeGet.js +78 -0
  38. package/src/handlers/correlations/correlationsDeveloperGet.js +226 -0
  39. package/src/handlers/correlations/correlationsGet.js +93 -0
  40. package/src/handlers/correlations/correlationsProjectGet.js +161 -0
  41. package/src/handlers/github/githubConnectionStatus.js +49 -0
  42. package/src/handlers/github/githubDiscoverPatterns.js +364 -0
  43. package/src/handlers/github/githubOAuthCallback.js +166 -0
  44. package/src/handlers/github/githubOAuthStart.js +59 -0
  45. package/src/handlers/github/githubPatternsReview.js +109 -0
  46. package/src/handlers/github/githubReposList.js +105 -0
  47. package/src/handlers/helpers/checkSuperAdmin.js +85 -0
  48. package/src/handlers/helpers/dbOperations.js +53 -0
  49. package/src/handlers/helpers/errorHandler.js +49 -0
  50. package/src/handlers/helpers/index.js +106 -0
  51. package/src/handlers/helpers/lambdaWrapper.js +60 -0
  52. package/src/handlers/helpers/responseUtil.js +55 -0
  53. package/src/handlers/helpers/subscriptionTiers.js +1168 -0
  54. package/src/handlers/notifications/getPreferences.js +84 -0
  55. package/src/handlers/notifications/sendNotification.js +170 -0
  56. package/src/handlers/notifications/updatePreferences.js +316 -0
  57. package/src/handlers/patterns/patternUsagePost.js +182 -0
  58. package/src/handlers/patterns/patternViolationPost.js +185 -0
  59. package/src/handlers/projects/projectCreate.js +107 -0
  60. package/src/handlers/projects/projectDelete.js +82 -0
  61. package/src/handlers/projects/projectGet.js +95 -0
  62. package/src/handlers/projects/projectUpdate.js +118 -0
  63. package/src/handlers/reports/aiLeverage.js +206 -0
  64. package/src/handlers/reports/engineeringInvestment.js +132 -0
  65. package/src/handlers/reports/riskForecast.js +186 -0
  66. package/src/handlers/reports/standardsRoi.js +162 -0
  67. package/src/handlers/scheduled/analyzeCorrelations.js +178 -0
  68. package/src/handlers/scheduled/analyzeGitHistory.js +510 -0
  69. package/src/handlers/scheduled/generateAlerts.js +135 -0
  70. package/src/handlers/scheduled/refreshActivity.js +21 -0
  71. package/src/handlers/scheduled/scanCompliance.js +334 -0
  72. package/src/handlers/sessions/sessionEndPost.js +180 -0
  73. package/src/handlers/sessions/sessionStandardsPost.js +135 -0
  74. package/src/handlers/stripe/addonManagePost.js +240 -0
  75. package/src/handlers/stripe/billingPortalPost.js +93 -0
  76. package/src/handlers/stripe/enterpriseCheckoutPost.js +272 -0
  77. package/src/handlers/stripe/seatsUpdatePost.js +185 -0
  78. package/src/handlers/stripe/subscriptionCancelDelete.js +169 -0
  79. package/src/handlers/stripe/subscriptionCreatePost.js +221 -0
  80. package/src/handlers/stripe/subscriptionUpdatePut.js +163 -0
  81. package/src/handlers/stripe/webhookPost.js +454 -0
  82. package/src/handlers/users/cognitoPostConfirmation.js +150 -0
  83. package/src/handlers/users/userEntitlementsGet.js +89 -0
  84. package/src/handlers/users/userGet.js +114 -0
  85. package/src/handlers/webhooks/githubWebhook.js +223 -0
  86. package/src/index.js +969 -0
@@ -0,0 +1,392 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MindMeld - Claude Code Pre-Compact Hook
4
+ *
5
+ * Harvests patterns from session transcript and validates compliance.
6
+ * Runs before conversation compaction to learn from the session.
7
+ *
8
+ * Features:
9
+ * - LLM-powered pattern detection (when ANTHROPIC_API_KEY is set)
10
+ * - Regex fallback for fast pattern matching
11
+ * - Standards validation against .equilateral-standards
12
+ * - Pattern promotion tracking
13
+ *
14
+ * @equilateral_ai/mindmeld v3.0.0
15
+ */
16
+
17
+ const path = require('path');
18
+ const fs = require('fs').promises;
19
+
20
+ // LLM Pattern Detection (optional - requires ANTHROPIC_API_KEY)
21
+ let LLMPatternDetector = null;
22
+ try {
23
+ const llmModule = require('../src/core/LLMPatternDetector');
24
+ LLMPatternDetector = llmModule.LLMPatternDetector;
25
+ } catch (error) {
26
+ // LLM module not available - will use regex fallback
27
+ }
28
+
29
+ /**
30
+ * Main hook execution
31
+ * @param {Object} sessionTranscript - Claude Code session data
32
+ */
33
+ async function harvestPatterns(sessionTranscript) {
34
+ const startTime = Date.now();
35
+
36
+ try {
37
+ // Fast bail if MindMeld not configured
38
+ const hasMindmeld = await checkMindmeldConfiguration();
39
+ if (!hasMindmeld) {
40
+ return { skipped: true, reason: 'No MindMeld configuration' };
41
+ }
42
+
43
+ // Load MindmeldClient with graceful degradation
44
+ const { MindmeldClient } = require('../src/index');
45
+
46
+ const mindmeld = new MindmeldClient({
47
+ projectPath: process.cwd()
48
+ });
49
+
50
+ // Extract session metadata
51
+ const sessionId = sessionTranscript.sessionId || generateSessionId();
52
+ const userId = sessionTranscript.userId || process.env.USER || 'unknown';
53
+
54
+ // 1. Detect patterns from session (LLM-powered or regex fallback)
55
+ let patterns = [];
56
+ let llmAnalysis = null;
57
+ // Bedrock uses AWS credentials, no API key needed
58
+ const useLLM = LLMPatternDetector && process.env.MINDMELD_USE_LLM !== 'false';
59
+
60
+ if (useLLM) {
61
+ // Use LLM for semantic pattern detection via AWS Bedrock
62
+ const detector = new LLMPatternDetector({
63
+ enabled: true,
64
+ model: process.env.MINDMELD_LLM_MODEL || 'us.anthropic.claude-3-5-haiku-20241022-v1:0',
65
+ region: process.env.AWS_REGION || 'us-east-2'
66
+ });
67
+
68
+ const transcriptText = typeof sessionTranscript === 'string'
69
+ ? sessionTranscript
70
+ : sessionTranscript.transcript || JSON.stringify(sessionTranscript);
71
+
72
+ llmAnalysis = await detector.analyzeSessionTranscript(transcriptText, {
73
+ projectName: path.basename(process.cwd()),
74
+ filesChanged: sessionTranscript.filesChanged || []
75
+ });
76
+
77
+ if (llmAnalysis.success && llmAnalysis.patterns) {
78
+ patterns = llmAnalysis.patterns.map(p => ({
79
+ element: p.element,
80
+ type: p.type,
81
+ intent: p.intent,
82
+ category: p.category,
83
+ confidence: p.confidence,
84
+ evidence: p.evidence,
85
+ source: 'llm'
86
+ }));
87
+
88
+ console.error(`[MindMeld] LLM detected ${patterns.length} patterns (model: ${llmAnalysis.model})`);
89
+ }
90
+ }
91
+
92
+ // Fallback to regex-based detection if LLM unavailable or failed
93
+ if (patterns.length === 0) {
94
+ patterns = await mindmeld.detectPatternsFromSession(sessionTranscript);
95
+ }
96
+
97
+ if (!patterns || patterns.length === 0) {
98
+ return {
99
+ patternsDetected: 0,
100
+ violations: 0,
101
+ reinforced: 0,
102
+ llmUsed: useLLM && llmAnalysis?.success,
103
+ elapsed: Date.now() - startTime
104
+ };
105
+ }
106
+
107
+ // 2. Validate against standards
108
+ const validationResults = await validatePatterns(mindmeld, patterns);
109
+
110
+ // 3. Record violations
111
+ for (const result of validationResults.violations) {
112
+ await mindmeld.recordViolation({
113
+ pattern: result.pattern,
114
+ violations: result.violations,
115
+ sessionId: sessionId,
116
+ userId: userId
117
+ });
118
+ }
119
+
120
+ // 4. Reinforce valid patterns
121
+ for (const result of validationResults.valid) {
122
+ await mindmeld.reinforcePattern({
123
+ pattern: result.pattern,
124
+ sessionId: sessionId,
125
+ userId: userId
126
+ });
127
+ }
128
+
129
+ // 5. Check for promotion candidates
130
+ const candidates = await checkPromotionCandidates(mindmeld, validationResults.valid);
131
+
132
+ // 6. Check README staleness (documentation maintenance)
133
+ let readmeStatus = null;
134
+ try {
135
+ const { checkReadmeStaleness } = require('../scripts/check-readme-staleness');
136
+ readmeStatus = await checkReadmeStaleness(process.cwd());
137
+
138
+ if (readmeStatus.stale) {
139
+ console.error(`[MindMeld] 📄 README staleness detected:`);
140
+ console.error(`[MindMeld] Changes since last update: ${readmeStatus.changes}`);
141
+ console.error(`[MindMeld] Agent changes: ${readmeStatus.analysis.agentChanges}`);
142
+ console.error(`[MindMeld] Standards changes: ${readmeStatus.analysis.standardsChanges}`);
143
+ console.error(`[MindMeld] Significance: ${readmeStatus.analysis.significance}`);
144
+
145
+ if (readmeStatus.shouldTrigger) {
146
+ console.error(`[MindMeld] 🔧 Critical changes detected - README update recommended`);
147
+ console.error(`[MindMeld] Run: node -e "const L=require('./src/agents/specialists/LibrarianAgent');new L().generateProjectReadme({projectRoot:process.cwd()})"`);
148
+ }
149
+ }
150
+ } catch (error) {
151
+ // Non-fatal - don't break pattern harvesting
152
+ console.error(`[MindMeld] README check skipped:`, error.message);
153
+ }
154
+
155
+ // 7. Log results
156
+ const elapsed = Date.now() - startTime;
157
+ const summary = {
158
+ patternsDetected: patterns.length,
159
+ violations: validationResults.violations.length,
160
+ reinforced: validationResults.valid.length,
161
+ promotionCandidates: candidates.length,
162
+ readmeStale: readmeStatus ? readmeStatus.stale : null,
163
+ readmeUpdateRecommended: readmeStatus ? readmeStatus.shouldTrigger : false,
164
+ llmUsed: useLLM && llmAnalysis?.success,
165
+ llmModel: llmAnalysis?.model || null,
166
+ llmSummary: llmAnalysis?.summary || null,
167
+ llmRecommendations: llmAnalysis?.recommendations || [],
168
+ elapsed: elapsed
169
+ };
170
+
171
+ console.error(`[MindMeld] Pattern harvesting complete in ${elapsed}ms`);
172
+ console.error(`[MindMeld] Detected: ${summary.patternsDetected}, Violations: ${summary.violations}, Reinforced: ${summary.reinforced}`);
173
+
174
+ if (candidates.length > 0) {
175
+ console.error(`[MindMeld] ${candidates.length} pattern(s) eligible for standards promotion:`);
176
+ for (const candidate of candidates) {
177
+ console.error(` - ${candidate.element} (correlation: ${(candidate.correlation * 100).toFixed(0)}%)`);
178
+ }
179
+ }
180
+
181
+ return summary;
182
+
183
+ } catch (error) {
184
+ // Graceful degradation - never block compaction
185
+ console.error('[MindMeld] Hook error (non-fatal):', error.message);
186
+ return {
187
+ error: error.message,
188
+ patternsDetected: 0,
189
+ violations: 0,
190
+ reinforced: 0
191
+ };
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Check if MindMeld is configured for this project
197
+ */
198
+ async function checkMindmeldConfiguration() {
199
+ try {
200
+ const mindmeldConfig = path.join(process.cwd(), '.mindmeld', 'config.json');
201
+ await fs.access(mindmeldConfig);
202
+ return true;
203
+ } catch {
204
+ return false;
205
+ }
206
+ }
207
+
208
+ /**
209
+ * Validate patterns against standards
210
+ */
211
+ async function validatePatterns(mindmeld, patterns) {
212
+ const violations = [];
213
+ const valid = [];
214
+
215
+ for (const pattern of patterns) {
216
+ const validation = await mindmeld.validatePattern(pattern);
217
+
218
+ if (!validation.valid) {
219
+ violations.push({
220
+ pattern: pattern,
221
+ violations: validation.violations
222
+ });
223
+ } else {
224
+ valid.push({
225
+ pattern: pattern
226
+ });
227
+ }
228
+ }
229
+
230
+ return { violations, valid };
231
+ }
232
+
233
+ /**
234
+ * Check for patterns eligible for promotion to standards
235
+ */
236
+ async function checkPromotionCandidates(mindmeld, validPatterns) {
237
+ const candidates = [];
238
+
239
+ for (const result of validPatterns) {
240
+ const eligible = await mindmeld.evaluateForPromotion(result.pattern);
241
+
242
+ if (eligible) {
243
+ candidates.push(eligible);
244
+ }
245
+ }
246
+
247
+ return candidates;
248
+ }
249
+
250
+ /**
251
+ * Generate session ID
252
+ */
253
+ function generateSessionId() {
254
+ return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
255
+ }
256
+
257
+ /**
258
+ * Parse session transcript if provided as string
259
+ */
260
+ function parseSessionTranscript(input) {
261
+ if (typeof input === 'string') {
262
+ try {
263
+ return JSON.parse(input);
264
+ } catch {
265
+ // Treat as raw transcript text
266
+ return {
267
+ transcript: input,
268
+ sessionId: generateSessionId()
269
+ };
270
+ }
271
+ }
272
+ return input;
273
+ }
274
+
275
+ /**
276
+ * Generate context summary to survive compaction
277
+ * This output goes to stdout and becomes part of the compacted summary
278
+ */
279
+ async function generatePostCompactContext(summary, llmAnalysis) {
280
+ const fsSync = require('fs');
281
+ const sections = [];
282
+
283
+ sections.push('# MindMeld Context Summary');
284
+ sections.push(`**Session harvested**: ${new Date().toISOString()}`);
285
+ sections.push(`**Project**: ${path.basename(process.cwd())}`);
286
+ sections.push('');
287
+
288
+ // Include session stats
289
+ sections.push('## Session Learning');
290
+ sections.push(`- Patterns detected: ${summary.patternsDetected || 0}`);
291
+ sections.push(`- Standards violations: ${summary.violations || 0}`);
292
+ sections.push(`- Patterns reinforced: ${summary.reinforced || 0}`);
293
+ if (summary.promotionCandidates > 0) {
294
+ sections.push(`- Promotion candidates: ${summary.promotionCandidates}`);
295
+ }
296
+ sections.push('');
297
+
298
+ // Include LLM recommendations if available
299
+ if (llmAnalysis?.recommendations && llmAnalysis.recommendations.length > 0) {
300
+ sections.push('## Session Recommendations');
301
+ for (const rec of llmAnalysis.recommendations) {
302
+ sections.push(`- ${rec}`);
303
+ }
304
+ sections.push('');
305
+ }
306
+
307
+ // Include LLM summary if available
308
+ if (llmAnalysis?.summary) {
309
+ sections.push('## Session Summary');
310
+ sections.push(llmAnalysis.summary);
311
+ sections.push('');
312
+ }
313
+
314
+ // Load relevant standards from .equilateral-standards if available
315
+ const standardsPath = path.join(process.cwd(), '.equilateral-standards');
316
+ try {
317
+ await fs.access(standardsPath);
318
+
319
+ // Load top-level standards index if available
320
+ const indexPath = path.join(standardsPath, 'index.json');
321
+ try {
322
+ const indexContent = await fs.readFile(indexPath, 'utf-8');
323
+ const index = JSON.parse(indexContent);
324
+
325
+ if (index.categories && index.categories.length > 0) {
326
+ sections.push('## Available Standards Categories');
327
+ for (const cat of index.categories.slice(0, 5)) {
328
+ sections.push(`- ${cat.name}: ${cat.description || ''}`);
329
+ }
330
+ sections.push('');
331
+ }
332
+ } catch {
333
+ // No index file - that's OK
334
+ }
335
+ } catch {
336
+ // No standards directory - that's OK
337
+ }
338
+
339
+ // Load project-specific patterns from .mindmeld if available
340
+ const mindmeldConfigPath = path.join(process.cwd(), '.mindmeld', 'config.json');
341
+ try {
342
+ const configContent = await fs.readFile(mindmeldConfigPath, 'utf-8');
343
+ const config = JSON.parse(configContent);
344
+
345
+ if (config.project_name) {
346
+ sections.push(`## Project: ${config.project_name}`);
347
+ }
348
+ if (config.team && config.team.length > 0) {
349
+ sections.push('**Team**: ' + config.team.map(t => t.name || t.email).join(', '));
350
+ }
351
+ sections.push('');
352
+ } catch {
353
+ // No mindmeld config - that's OK
354
+ }
355
+
356
+ sections.push('---');
357
+ sections.push('*Context preserved by MindMeld pre-compact hook - mindmeld.dev*');
358
+
359
+ return sections.join('\n');
360
+ }
361
+
362
+ // Execute if called directly
363
+ if (require.main === module) {
364
+ // Read session transcript from stdin or args
365
+ const input = process.argv[2];
366
+
367
+ if (!input) {
368
+ console.error('[MindMeld] Usage: pre-compact.js <session-transcript-json>');
369
+ process.exit(0);
370
+ }
371
+
372
+ const sessionTranscript = parseSessionTranscript(input);
373
+
374
+ harvestPatterns(sessionTranscript)
375
+ .then(async (result) => {
376
+ // Generate and output context summary for post-compaction injection
377
+ const postCompactContext = await generatePostCompactContext(
378
+ result,
379
+ result.llmAnalysis || { summary: result.llmSummary, recommendations: result.llmRecommendations }
380
+ );
381
+
382
+ // Output to stdout - this survives compaction
383
+ console.log(postCompactContext);
384
+ process.exit(0);
385
+ })
386
+ .catch(error => {
387
+ console.error('[MindMeld] Fatal error:', error);
388
+ process.exit(0); // Don't block compaction
389
+ });
390
+ }
391
+
392
+ module.exports = { harvestPatterns, parseSessionTranscript, generatePostCompactContext };
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MindMeld - Claude Code Session Start Hook
4
+ *
5
+ * Injects ONLY relevant standards and team patterns at session start.
6
+ * Goal: < 500ms execution time for optimal UX.
7
+ *
8
+ * @equilateral_ai/mindmeld v3.0.0
9
+ */
10
+
11
+ const path = require('path');
12
+ const fs = require('fs').promises;
13
+
14
+ /**
15
+ * Generate a fingerprint for standards injection tracking
16
+ * @param {string} userId - User identifier
17
+ * @param {string} companyId - Company identifier
18
+ * @param {string} tier - Subscription tier
19
+ * @returns {string} Base64-encoded fingerprint wrapped in invisible comment
20
+ */
21
+ function generateFingerprint(userId, companyId, tier) {
22
+ const fingerprint = {
23
+ user_id: userId || 'anonymous',
24
+ company_id: companyId || 'unknown',
25
+ timestamp: new Date().toISOString(),
26
+ subscription_tier: tier || 'free'
27
+ };
28
+
29
+ const base64Fingerprint = Buffer.from(JSON.stringify(fingerprint)).toString('base64');
30
+ return `<!-- fp:${base64Fingerprint} -->`;
31
+ }
32
+
33
+ /**
34
+ * Load user and company info from MindMeld config
35
+ * @returns {Promise<{userId: string, companyId: string, tier: string}>}
36
+ */
37
+ async function loadFingerprintConfig() {
38
+ try {
39
+ const configPath = path.join(process.cwd(), '.mindmeld', 'config.json');
40
+ const configContent = await fs.readFile(configPath, 'utf-8');
41
+ const config = JSON.parse(configContent);
42
+
43
+ return {
44
+ userId: config.user_id || process.env.MINDMELD_USER_ID || 'anonymous',
45
+ companyId: config.company_id || process.env.MINDMELD_COMPANY_ID || 'unknown',
46
+ tier: config.subscription_tier || process.env.MINDMELD_TIER || 'free'
47
+ };
48
+ } catch {
49
+ // Fallback to environment variables
50
+ return {
51
+ userId: process.env.MINDMELD_USER_ID || 'anonymous',
52
+ companyId: process.env.MINDMELD_COMPANY_ID || 'unknown',
53
+ tier: process.env.MINDMELD_TIER || 'free'
54
+ };
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Main hook execution
60
+ */
61
+ async function injectContext() {
62
+ const startTime = Date.now();
63
+
64
+ try {
65
+ // Fast bail if MindMeld not configured
66
+ const hasMindmeld = await checkMindmeldConfiguration();
67
+ if (!hasMindmeld) {
68
+ return '';
69
+ }
70
+
71
+ // Load fingerprint config
72
+ const fingerprintConfig = await loadFingerprintConfig();
73
+
74
+ // Load MindmeldClient with graceful degradation
75
+ const { MindmeldClient } = require('../src/index');
76
+
77
+ const mindmeld = new MindmeldClient({
78
+ projectPath: process.cwd(),
79
+ standardsPath: path.join(process.cwd(), '.equilateral-standards')
80
+ });
81
+
82
+ // 1. Ensure standards are ingested (cached, fast check)
83
+ await mindmeld.ensureStandardsIngested();
84
+
85
+ // 2. Detect project context
86
+ const context = await mindmeld.detectProject();
87
+
88
+ if (!context) {
89
+ return ''; // No MindMeld project
90
+ }
91
+
92
+ // 3. Generate session ID for tracking
93
+ const sessionId = mindmeld.generateSessionId();
94
+
95
+ // 4. Identify relevant standards (NOT all 80+)
96
+ const relevance = await mindmeld.getRelevantStandards(context);
97
+ const relevantStandards = relevance.standards.slice(0, 10); // Top 10 most relevant
98
+
99
+ // 5. Record standards shown (Phase 7 - fire and forget, non-blocking)
100
+ mindmeld.recordStandardsShown(sessionId, relevantStandards);
101
+
102
+ // 6. Load team patterns
103
+ const teamPatterns = await mindmeld.loadProjectContext(context.projectId);
104
+
105
+ // 7. Get recent learning (last 7 days)
106
+ const recentLearning = await mindmeld.getRecentLearning(context.projectId, 7);
107
+
108
+ // 8. Build context injection with fingerprint
109
+ const injection = formatContextInjection({
110
+ project: context.projectName,
111
+ sessionId: sessionId,
112
+ collaborators: context.collaborators,
113
+ relevantStandards: relevantStandards,
114
+ teamPatterns: teamPatterns ? teamPatterns.filter(p => p.correlation > 0.7) : [],
115
+ recentLearning: recentLearning || [],
116
+ fingerprint: fingerprintConfig
117
+ });
118
+
119
+ const elapsed = Date.now() - startTime;
120
+ console.error(`[MindMeld] Context injected in ${elapsed}ms`);
121
+
122
+ return injection;
123
+
124
+ } catch (error) {
125
+ // Graceful degradation - never block Claude Code session
126
+ console.error('[MindMeld] Hook error (non-fatal):', error.message);
127
+ return '';
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Check if MindMeld is configured for this project
133
+ */
134
+ async function checkMindmeldConfiguration() {
135
+ try {
136
+ const mindmeldConfig = path.join(process.cwd(), '.mindmeld', 'config.json');
137
+ await fs.access(mindmeldConfig);
138
+ return true;
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Format context injection for Claude Code
146
+ * @param {object} data - Context data to inject
147
+ * @param {object} data.fingerprint - Fingerprint config {userId, companyId, tier}
148
+ */
149
+ function formatContextInjection(data) {
150
+ const {
151
+ project,
152
+ sessionId,
153
+ collaborators,
154
+ relevantStandards,
155
+ teamPatterns,
156
+ recentLearning,
157
+ fingerprint
158
+ } = data;
159
+
160
+ const sections = [];
161
+
162
+ // Generate fingerprint string
163
+ const fingerprintStr = fingerprint
164
+ ? generateFingerprint(fingerprint.userId, fingerprint.companyId, fingerprint.tier)
165
+ : '';
166
+
167
+ // Header with session ID for tracking
168
+ sections.push(`# MindMeld Context - ${project}`);
169
+ if (sessionId) {
170
+ sections.push(`<!-- session:${sessionId} -->`);
171
+ }
172
+ sections.push('');
173
+
174
+ // Copyright/License notice
175
+ sections.push('© 2025 Equilateral AI (Pareidolia LLC). All rights reserved.');
176
+ sections.push('Licensed for use within MindMeld platform only. Redistribution prohibited.');
177
+ sections.push('');
178
+
179
+ // Collaborators
180
+ if (collaborators && collaborators.length > 0) {
181
+ sections.push('## Team');
182
+ sections.push(collaborators.map(c => `- ${c.name} (${c.email})`).join('\n'));
183
+ sections.push('');
184
+ }
185
+
186
+ // Relevant standards (top 10 only)
187
+ if (relevantStandards && relevantStandards.length > 0) {
188
+ sections.push('## Relevant Standards');
189
+ sections.push('');
190
+
191
+ for (const standard of relevantStandards) {
192
+ sections.push(`### ${standard.element}`);
193
+ sections.push(`**Category**: ${standard.category}`);
194
+ // Add fingerprint to rule text
195
+ sections.push(`**Rule**: ${standard.rule} ${fingerprintStr}`);
196
+
197
+ if (standard.examples && standard.examples.length > 0) {
198
+ sections.push('');
199
+ sections.push('**Example**:');
200
+ sections.push('```javascript');
201
+ sections.push(standard.examples[0].code);
202
+ sections.push('```');
203
+ }
204
+
205
+ if (standard.anti_patterns && standard.anti_patterns.length > 0) {
206
+ sections.push('');
207
+ sections.push('**Anti-patterns**:');
208
+ for (const antiPattern of standard.anti_patterns) {
209
+ sections.push(`- ❌ ${antiPattern.description}`);
210
+ }
211
+ }
212
+
213
+ sections.push('');
214
+ }
215
+ }
216
+
217
+ // Team patterns (high correlation only)
218
+ if (teamPatterns && teamPatterns.length > 0) {
219
+ sections.push('## Team Patterns');
220
+ sections.push('');
221
+
222
+ for (const pattern of teamPatterns) {
223
+ sections.push(`### ${pattern.element}`);
224
+ sections.push(`**Correlation**: ${(pattern.correlation * 100).toFixed(0)}%`);
225
+ sections.push(`**Usage**: ${pattern.usage_count} times across ${pattern.project_count} projects`);
226
+ // Add fingerprint to rule text
227
+ sections.push(`**Rule**: ${pattern.rule} ${fingerprintStr}`);
228
+ sections.push('');
229
+ }
230
+ }
231
+
232
+ // Recent learning
233
+ if (recentLearning && recentLearning.length > 0) {
234
+ sections.push('## Recent Team Learning');
235
+ sections.push('');
236
+
237
+ for (const learning of recentLearning) {
238
+ sections.push(`- **${learning.pattern}**: ${learning.insight} (${learning.date})`);
239
+ }
240
+ sections.push('');
241
+ }
242
+
243
+ sections.push('---');
244
+ sections.push('*Context provided by MindMeld - mindmeld.dev*');
245
+
246
+ return sections.join('\n');
247
+ }
248
+
249
+ // Execute if called directly
250
+ if (require.main === module) {
251
+ injectContext()
252
+ .then(context => {
253
+ if (context) {
254
+ console.log(context);
255
+ }
256
+ process.exit(0);
257
+ })
258
+ .catch(error => {
259
+ console.error('[MindMeld] Fatal error:', error);
260
+ process.exit(0); // Don't block session start
261
+ });
262
+ }
263
+
264
+ module.exports = { injectContext, formatContextInjection, generateFingerprint };