@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,388 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MindMeld CLI - Inject relevant standards into AI coding tool config
4
+ *
5
+ * Usage:
6
+ * mindmeld inject # stdout (raw markdown)
7
+ * mindmeld inject --format cursorrules
8
+ * mindmeld inject --format windsurfrules
9
+ * mindmeld inject --format aider
10
+ * mindmeld inject --format claude # Claude Code hook format
11
+ *
12
+ * @equilateral_ai/mindmeld v3.0.0
13
+ */
14
+
15
+ const path = require('path');
16
+ const fs = require('fs').promises;
17
+
18
+ const FORMATS = {
19
+ raw: { file: null, description: 'Raw markdown to stdout' },
20
+ cursorrules: { file: '.cursorrules', description: 'Cursor rules file' },
21
+ windsurfrules: { file: '.windsurfrules', description: 'Windsurf rules file' },
22
+ aider: { file: '.aider.conventions.md', description: 'Aider conventions file' },
23
+ claude: { file: null, description: 'Claude Code hook format (stdout)' }
24
+ };
25
+
26
+ function showInjectHelp() {
27
+ console.log(`
28
+ MindMeld inject - Generate context-aware standards for any AI coding tool
29
+
30
+ Usage:
31
+ mindmeld inject [options]
32
+
33
+ Options:
34
+ --format <type> Output format (default: raw)
35
+ --path <dir> Project path (default: current directory)
36
+ --help, -h Show this help
37
+
38
+ Formats:
39
+ raw Plain markdown to stdout (pipe anywhere)
40
+ cursorrules Write .cursorrules file
41
+ windsurfrules Write .windsurfrules file
42
+ aider Write .aider.conventions.md file
43
+ claude Claude Code hook format (stdout)
44
+
45
+ Examples:
46
+ mindmeld inject # Print standards to stdout
47
+ mindmeld inject --format cursorrules # Update .cursorrules
48
+ mindmeld inject --format aider # Update aider conventions
49
+ mindmeld inject | ollama run qwen3-coder # Pipe to local model
50
+ mindmeld inject --format raw >> system.txt # Append to system prompt
51
+ `);
52
+ }
53
+
54
+ /**
55
+ * Detect project context by scanning files in the working directory
56
+ */
57
+ async function detectProjectContext(projectPath) {
58
+ const context = {
59
+ projectName: path.basename(projectPath),
60
+ languages: [],
61
+ frameworks: [],
62
+ files: []
63
+ };
64
+
65
+ // Check for common project indicators
66
+ const checks = [
67
+ { file: 'package.json', language: 'javascript', frameworks: ['node'] },
68
+ { file: 'tsconfig.json', language: 'typescript', frameworks: ['node'] },
69
+ { file: 'template.yaml', language: 'yaml', frameworks: ['aws-sam'] },
70
+ { file: 'serverless.yml', language: 'yaml', frameworks: ['serverless'] },
71
+ { file: 'Dockerfile', language: 'docker', frameworks: ['docker'] },
72
+ { file: 'requirements.txt', language: 'python', frameworks: [] },
73
+ { file: 'go.mod', language: 'go', frameworks: [] },
74
+ { file: 'Cargo.toml', language: 'rust', frameworks: [] },
75
+ { file: '.terraform', language: 'hcl', frameworks: ['terraform'] }
76
+ ];
77
+
78
+ for (const check of checks) {
79
+ try {
80
+ await fs.access(path.join(projectPath, check.file));
81
+ if (!context.languages.includes(check.language)) {
82
+ context.languages.push(check.language);
83
+ }
84
+ context.frameworks.push(...check.frameworks);
85
+ context.files.push(check.file);
86
+ } catch {
87
+ // File doesn't exist
88
+ }
89
+ }
90
+
91
+ // Check for specific patterns in package.json
92
+ try {
93
+ const pkg = JSON.parse(await fs.readFile(path.join(projectPath, 'package.json'), 'utf-8'));
94
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
95
+ if (deps['react'] || deps['next']) context.frameworks.push('react');
96
+ if (deps['express'] || deps['fastify']) context.frameworks.push('api');
97
+ if (deps['@aws-sdk']) context.frameworks.push('aws');
98
+ if (deps['pg'] || deps['mysql2'] || deps['mongoose']) context.frameworks.push('database');
99
+ } catch {
100
+ // No package.json or can't parse
101
+ }
102
+
103
+ return context;
104
+ }
105
+
106
+ /**
107
+ * Load and score standards from .equilateral-standards
108
+ */
109
+ async function getRelevantStandards(projectPath, context) {
110
+ const standardsDir = path.join(projectPath, '.equilateral-standards');
111
+ const standards = [];
112
+
113
+ try {
114
+ await fs.access(standardsDir);
115
+ } catch {
116
+ return [];
117
+ }
118
+
119
+ // Recursively find all .md standard files from community repo
120
+ async function scanDir(dir) {
121
+ const entries = await fs.readdir(dir, { withFileTypes: true });
122
+ for (const entry of entries) {
123
+ const fullPath = path.join(dir, entry.name);
124
+ if (entry.isDirectory()) {
125
+ await scanDir(fullPath);
126
+ } else if (entry.name.endsWith('.md') && entry.name !== 'README.md') {
127
+ try {
128
+ const content = await fs.readFile(fullPath, 'utf-8');
129
+ const relativePath = path.relative(standardsDir, fullPath);
130
+ const category = path.dirname(relativePath).replace(/\//g, '/');
131
+ standards.push({
132
+ element: entry.name.replace('.md', '').replace(/_/g, ' '),
133
+ category: category === '.' ? 'general' : category,
134
+ content: content,
135
+ path: relativePath,
136
+ score: 0
137
+ });
138
+ } catch {
139
+ // Skip unreadable files
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ await scanDir(standardsDir);
146
+
147
+ // Score relevance based on project context
148
+ for (const standard of standards) {
149
+ let score = 0;
150
+ const contentLower = standard.content.toLowerCase();
151
+ const categoryLower = standard.category.toLowerCase();
152
+
153
+ // Language match
154
+ for (const lang of context.languages) {
155
+ if (contentLower.includes(lang) || categoryLower.includes(lang)) {
156
+ score += 3;
157
+ }
158
+ }
159
+
160
+ // Framework match
161
+ for (const fw of context.frameworks) {
162
+ if (contentLower.includes(fw) || categoryLower.includes(fw)) {
163
+ score += 5;
164
+ }
165
+ }
166
+
167
+ // Category relevance boost
168
+ if (categoryLower.includes('serverless') && context.frameworks.includes('aws-sam')) score += 4;
169
+ if (categoryLower.includes('api') && context.frameworks.includes('api')) score += 4;
170
+ if (categoryLower.includes('database') && context.frameworks.includes('database')) score += 4;
171
+ if (categoryLower.includes('react') && context.frameworks.includes('react')) score += 4;
172
+ if (categoryLower.includes('cost') && context.frameworks.includes('aws')) score += 3;
173
+
174
+ // Base score for all standards (everyone gets some relevance)
175
+ score += 1;
176
+
177
+ standard.score = score;
178
+ }
179
+
180
+ // Sort by relevance, return top 10
181
+ standards.sort((a, b) => b.score - a.score);
182
+ return standards.slice(0, 10);
183
+ }
184
+
185
+ /**
186
+ * Load team patterns from .mindmeld config
187
+ */
188
+ async function loadTeamPatterns(projectPath) {
189
+ try {
190
+ const configPath = path.join(projectPath, '.mindmeld', 'config.json');
191
+ const config = JSON.parse(await fs.readFile(configPath, 'utf-8'));
192
+ return {
193
+ projectName: config.projectName || path.basename(projectPath),
194
+ collaborators: config.collaborators || [],
195
+ team: config.team || false
196
+ };
197
+ } catch {
198
+ return {
199
+ projectName: path.basename(projectPath),
200
+ collaborators: [],
201
+ team: false
202
+ };
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Format standards as raw markdown
208
+ */
209
+ function formatRaw(standards, projectInfo, context) {
210
+ const sections = [];
211
+
212
+ sections.push(`# Standards for ${projectInfo.projectName}`);
213
+ sections.push(`<!-- Generated by MindMeld | ${new Date().toISOString()} -->`);
214
+ sections.push(`<!-- Context: ${context.languages.join(', ')} | ${context.frameworks.join(', ')} -->`);
215
+ sections.push('');
216
+
217
+ if (standards.length === 0) {
218
+ sections.push('No standards found. Run `mindmeld init` to set up your project.');
219
+ return sections.join('\n');
220
+ }
221
+
222
+ sections.push(`Injecting ${standards.length} relevant standards (of ${standards.length} scored):`);
223
+ sections.push('');
224
+
225
+ for (const standard of standards) {
226
+ sections.push(`## ${standard.element}`);
227
+ sections.push(`**Category**: ${standard.category} | **Relevance**: ${standard.score}`);
228
+ sections.push('');
229
+ // Truncate content to keep injection lean
230
+ const lines = standard.content.split('\n');
231
+ const truncated = lines.slice(0, 30).join('\n');
232
+ sections.push(truncated);
233
+ if (lines.length > 30) {
234
+ sections.push('...');
235
+ }
236
+ sections.push('');
237
+ }
238
+
239
+ sections.push('---');
240
+ sections.push('*Generated by MindMeld - mindmeld.dev*');
241
+
242
+ return sections.join('\n');
243
+ }
244
+
245
+ /**
246
+ * Format for .cursorrules / .windsurfrules (compact rules format)
247
+ */
248
+ function formatRulesFile(standards, projectInfo, context) {
249
+ const sections = [];
250
+
251
+ sections.push(`# Project Standards - ${projectInfo.projectName}`);
252
+ sections.push(`# Auto-generated by MindMeld (mindmeld.dev)`);
253
+ sections.push(`# Context: ${context.frameworks.join(', ') || context.languages.join(', ')}`);
254
+ sections.push(`# Run \`mindmeld inject --format ${context._format}\` to regenerate`);
255
+ sections.push('');
256
+
257
+ for (const standard of standards) {
258
+ sections.push(`## ${standard.element}`);
259
+ // Extract just the key rules, not full prose
260
+ const lines = standard.content.split('\n');
261
+ const ruleLines = lines.filter(l =>
262
+ l.startsWith('- ') || l.startsWith('* ') ||
263
+ l.startsWith(' - ') || l.startsWith('> ') ||
264
+ l.match(/^#{1,4}\s/) || l.match(/^\*\*[^*]+\*\*:/)
265
+ ).slice(0, 10);
266
+
267
+ if (ruleLines.length > 0) {
268
+ sections.push(ruleLines.join('\n'));
269
+ } else {
270
+ // Fallback: first meaningful paragraph
271
+ const meaningful = lines.filter(l => l.trim().length > 20 && !l.startsWith('#')).slice(0, 3);
272
+ sections.push(meaningful.join('\n'));
273
+ }
274
+ sections.push('');
275
+ }
276
+
277
+ return sections.join('\n');
278
+ }
279
+
280
+ /**
281
+ * Format for aider conventions
282
+ */
283
+ function formatAider(standards, projectInfo, context) {
284
+ const sections = [];
285
+
286
+ sections.push(`# Coding Conventions - ${projectInfo.projectName}`);
287
+ sections.push('');
288
+ sections.push(`> Auto-generated by MindMeld (mindmeld.dev)`);
289
+ sections.push(`> Run \`mindmeld inject --format aider\` to regenerate`);
290
+ sections.push('');
291
+
292
+ for (const standard of standards) {
293
+ sections.push(`## ${standard.element}`);
294
+ sections.push('');
295
+ // Aider conventions prefer concise bullet points
296
+ const lines = standard.content.split('\n');
297
+ const bullets = lines.filter(l =>
298
+ l.startsWith('- ') || l.startsWith('* ') || l.match(/^\*\*[^*]+\*\*:/)
299
+ ).slice(0, 8);
300
+
301
+ if (bullets.length > 0) {
302
+ sections.push(bullets.join('\n'));
303
+ } else {
304
+ const meaningful = lines.filter(l => l.trim().length > 10 && !l.startsWith('#')).slice(0, 4);
305
+ sections.push(meaningful.join('\n'));
306
+ }
307
+ sections.push('');
308
+ }
309
+
310
+ return sections.join('\n');
311
+ }
312
+
313
+ /**
314
+ * Main inject execution
315
+ */
316
+ async function inject(options = {}) {
317
+ const format = options.format || 'raw';
318
+ const projectPath = options.path || process.cwd();
319
+
320
+ if (!FORMATS[format]) {
321
+ console.error(`Unknown format: ${format}`);
322
+ console.error(`Available: ${Object.keys(FORMATS).join(', ')}`);
323
+ process.exit(1);
324
+ }
325
+
326
+ // Standalone detection (no database, no prompts, works everywhere)
327
+ let standards = [];
328
+ let projectInfo = {};
329
+ let context = {};
330
+
331
+ context = await detectProjectContext(projectPath);
332
+ standards = await getRelevantStandards(projectPath, context);
333
+ projectInfo = await loadTeamPatterns(projectPath);
334
+
335
+ context._format = format;
336
+
337
+ // 2. Format output
338
+ let output;
339
+ switch (format) {
340
+ case 'cursorrules':
341
+ case 'windsurfrules':
342
+ output = formatRulesFile(standards, projectInfo, context);
343
+ break;
344
+ case 'aider':
345
+ output = formatAider(standards, projectInfo, context);
346
+ break;
347
+ case 'claude':
348
+ // Use the session-start formatter with mapped fields
349
+ try {
350
+ const { formatContextInjection } = require('../hooks/session-start');
351
+ const mappedStandards = standards.map(s => ({
352
+ element: s.element,
353
+ category: s.category,
354
+ rule: s.content.split('\n').find(l => l.trim().length > 10 && !l.startsWith('#')) || s.element
355
+ }));
356
+ output = formatContextInjection({
357
+ project: projectInfo.projectName,
358
+ relevantStandards: mappedStandards,
359
+ teamPatterns: [],
360
+ recentLearning: [],
361
+ collaborators: projectInfo.collaborators || []
362
+ });
363
+ } catch {
364
+ output = formatRaw(standards, projectInfo, context);
365
+ }
366
+ break;
367
+ case 'raw':
368
+ default:
369
+ output = formatRaw(standards, projectInfo, context);
370
+ break;
371
+ }
372
+
373
+ // 3. Write to file or stdout
374
+ const targetFile = FORMATS[format].file;
375
+
376
+ if (targetFile) {
377
+ const targetPath = path.join(projectPath, targetFile);
378
+ await fs.writeFile(targetPath, output);
379
+ console.error(`[MindMeld] Written ${standards.length} standards to ${targetFile}`);
380
+ console.error(`[MindMeld] Context: ${context.languages ? context.languages.join(', ') : 'unknown'} | ${context.frameworks ? context.frameworks.join(', ') : ''}`);
381
+ console.error(`[MindMeld] Regenerate anytime: mindmeld inject --format ${format}`);
382
+ } else {
383
+ // Output to stdout (for piping)
384
+ console.log(output);
385
+ }
386
+ }
387
+
388
+ module.exports = { inject, showInjectHelp, FORMATS };