@equilateral_ai/mindmeld 3.1.2 → 3.2.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.
@@ -16,6 +16,7 @@
16
16
 
17
17
  const path = require('path');
18
18
  const fs = require('fs').promises;
19
+ const crypto = require('crypto');
19
20
 
20
21
  // LLM Pattern Detection (optional - requires ANTHROPIC_API_KEY)
21
22
  let LLMPatternDetector = null;
@@ -200,7 +201,11 @@ async function checkMindmeldConfiguration() {
200
201
  const mindmeldConfig = path.join(process.cwd(), '.mindmeld', 'config.json');
201
202
  await fs.access(mindmeldConfig);
202
203
  return true;
203
- } catch {
204
+ } catch (error) {
205
+ // Expected: config doesn't exist
206
+ if (error.code !== 'ENOENT') {
207
+ console.error('Unexpected error checking MindMeld config:', error.message);
208
+ }
204
209
  return false;
205
210
  }
206
211
  }
@@ -248,10 +253,10 @@ async function checkPromotionCandidates(mindmeld, validPatterns) {
248
253
  }
249
254
 
250
255
  /**
251
- * Generate session ID
256
+ * Generate session ID using crypto for consistency
252
257
  */
253
258
  function generateSessionId() {
254
- return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
259
+ return `session_${Date.now()}_${crypto.randomBytes(6).toString('hex')}`;
255
260
  }
256
261
 
257
262
  /**
@@ -261,8 +266,11 @@ function parseSessionTranscript(input) {
261
266
  if (typeof input === 'string') {
262
267
  try {
263
268
  return JSON.parse(input);
264
- } catch {
265
- // Treat as raw transcript text
269
+ } catch (error) {
270
+ // Expected: not valid JSON, treat as raw transcript text
271
+ if (!(error instanceof SyntaxError)) {
272
+ console.error('Unexpected error parsing transcript:', error.message);
273
+ }
266
274
  return {
267
275
  transcript: input,
268
276
  sessionId: generateSessionId()
@@ -329,11 +337,17 @@ async function generatePostCompactContext(summary, llmAnalysis) {
329
337
  }
330
338
  sections.push('');
331
339
  }
332
- } catch {
333
- // No index file - that's OK
340
+ } catch (error) {
341
+ // Expected: no index file
342
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
343
+ console.error('Unexpected error reading standards index:', error.message);
344
+ }
345
+ }
346
+ } catch (error) {
347
+ // Expected: no standards directory
348
+ if (error.code !== 'ENOENT') {
349
+ console.error('Unexpected error accessing standards:', error.message);
334
350
  }
335
- } catch {
336
- // No standards directory - that's OK
337
351
  }
338
352
 
339
353
  // Load project-specific patterns from .mindmeld if available
@@ -349,8 +363,11 @@ async function generatePostCompactContext(summary, llmAnalysis) {
349
363
  sections.push('**Team**: ' + config.team.map(t => t.name || t.email).join(', '));
350
364
  }
351
365
  sections.push('');
352
- } catch {
353
- // No mindmeld config - that's OK
366
+ } catch (error) {
367
+ // Expected: no mindmeld config
368
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
369
+ console.error('Unexpected error reading mindmeld config:', error.message);
370
+ }
354
371
  }
355
372
 
356
373
  sections.push('---');
@@ -45,7 +45,11 @@ async function loadFingerprintConfig() {
45
45
  companyId: config.company_id || process.env.MINDMELD_COMPANY_ID || 'unknown',
46
46
  tier: config.subscription_tier || process.env.MINDMELD_TIER || 'free'
47
47
  };
48
- } catch {
48
+ } catch (error) {
49
+ // Expected: config doesn't exist or invalid JSON
50
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
51
+ console.error('Unexpected error loading fingerprint config:', error.message);
52
+ }
49
53
  // Fallback to environment variables
50
54
  return {
51
55
  userId: process.env.MINDMELD_USER_ID || 'anonymous',
@@ -136,7 +140,11 @@ async function checkMindmeldConfiguration() {
136
140
  const mindmeldConfig = path.join(process.cwd(), '.mindmeld', 'config.json');
137
141
  await fs.access(mindmeldConfig);
138
142
  return true;
139
- } catch {
143
+ } catch (error) {
144
+ // Expected: config doesn't exist
145
+ if (error.code !== 'ENOENT') {
146
+ console.error('Unexpected error checking MindMeld config:', error.message);
147
+ }
140
148
  return false;
141
149
  }
142
150
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equilateral_ai/mindmeld",
3
- "version": "3.1.2",
3
+ "version": "3.2.0",
4
4
  "description": "Intelligent standards injection for AI coding sessions - context-aware, self-documenting, scales to large codebases",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  "scripts/init-project.js",
13
13
  "scripts/inject.js",
14
14
  "scripts/harvest.js",
15
+ "scripts/repo-analyzer.js",
15
16
  "README.md"
16
17
  ],
17
18
  "publishConfig": {
@@ -71,13 +71,27 @@ async function getGitHistory(projectPath, options = {}) {
71
71
  try {
72
72
  const { stdout: log } = await execAsync(
73
73
  `git log --since="${since}" -n ${maxCommits} --format="%H|%s|%an|%ai" --stat`,
74
- { cwd: projectPath, maxBuffer: 1024 * 1024 }
74
+ { cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }
75
75
  );
76
76
 
77
- const { stdout: diff } = await execAsync(
78
- `git log --since="${since}" -n ${maxCommits} -p --diff-filter=M`,
79
- { cwd: projectPath, maxBuffer: 5 * 1024 * 1024 }
80
- );
77
+ // For large repos, limit diff output to avoid buffer overflow
78
+ let diff = '';
79
+ try {
80
+ const { stdout } = await execAsync(
81
+ `git log --since="${since}" -n ${maxCommits} -p --diff-filter=M -- '*.js' '*.ts' '*.py' '*.java' '*.go' '*.rs'`,
82
+ { cwd: projectPath, maxBuffer: 20 * 1024 * 1024 }
83
+ );
84
+ diff = stdout;
85
+ } catch (diffError) {
86
+ // If diff is too large, try with fewer commits
87
+ if (diffError.message.includes('maxBuffer')) {
88
+ const { stdout } = await execAsync(
89
+ `git log --since="${since}" -n 10 -p --diff-filter=M -- '*.js' '*.ts'`,
90
+ { cwd: projectPath, maxBuffer: 10 * 1024 * 1024 }
91
+ );
92
+ diff = stdout;
93
+ }
94
+ }
81
95
 
82
96
  return { log, diff };
83
97
  } catch (error) {
@@ -268,8 +282,11 @@ async function harvest(options = {}) {
268
282
  console.error('\n[MindMeld] LLM-powered detection available for deeper analysis.');
269
283
  console.error('[MindMeld] Set MINDMELD_USE_LLM=true for semantic pattern detection.');
270
284
  }
271
- } catch {
272
- // LLM module not available
285
+ } catch (error) {
286
+ // Expected: LLM module not available
287
+ if (error.code !== 'MODULE_NOT_FOUND') {
288
+ console.error('Unexpected error loading LLM module:', error.message);
289
+ }
273
290
  }
274
291
  }
275
292
 
@@ -304,8 +321,11 @@ async function promotePatterns(projectPath, patterns, options = {}) {
304
321
  try {
305
322
  await fs.access(filePath);
306
323
  continue;
307
- } catch {
308
- // File doesn't exist, safe to create
324
+ } catch (error) {
325
+ // Expected: file doesn't exist, safe to create
326
+ if (error.code !== 'ENOENT') {
327
+ console.error(`Unexpected error checking ${filePath}:`, error.message);
328
+ }
309
329
  }
310
330
 
311
331
  const content = [
@@ -358,7 +378,11 @@ async function harvestPlans(projectPath) {
358
378
  const indexContent = await fs.readFile(sessionIndexPath, 'utf-8');
359
379
  const index = JSON.parse(indexContent);
360
380
  sessions = index.entries || [];
361
- } catch {
381
+ } catch (error) {
382
+ // Expected: no sessions file
383
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
384
+ console.error('Unexpected error reading sessions:', error.message);
385
+ }
362
386
  return []; // No sessions found for this project
363
387
  }
364
388
 
@@ -383,7 +407,11 @@ async function harvestPlans(projectPath) {
383
407
  planFiles.push({ path: filePath, name: entry, mtime });
384
408
  }
385
409
  }
386
- } catch {
410
+ } catch (error) {
411
+ // Expected: no plans directory
412
+ if (error.code !== 'ENOENT') {
413
+ console.error('Unexpected error reading plans:', error.message);
414
+ }
387
415
  return []; // No plans directory
388
416
  }
389
417
 
@@ -409,8 +437,11 @@ async function harvestPlans(projectPath) {
409
437
  try {
410
438
  await fs.access(path.join(projectPath, cleanRef));
411
439
  relevance += 3; // File exists in this project
412
- } catch {
413
- // File doesn't exist here
440
+ } catch (error) {
441
+ // Expected: file doesn't exist here
442
+ if (error.code !== 'ENOENT') {
443
+ console.error(`Unexpected error checking ${cleanRef}:`, error.message);
444
+ }
414
445
  }
415
446
  }
416
447
 
@@ -422,8 +453,11 @@ async function harvestPlans(projectPath) {
422
453
  planFile.content = content;
423
454
  relevantPlans.push(planFile);
424
455
  }
425
- } catch {
426
- // Skip unreadable
456
+ } catch (error) {
457
+ // Expected: file not readable
458
+ if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
459
+ console.error(`Unexpected error reading plan ${planFile.path}:`, error.message);
460
+ }
427
461
  }
428
462
  }
429
463
 
@@ -484,8 +518,11 @@ async function harvestPlans(projectPath) {
484
518
  });
485
519
  }
486
520
  }
487
- } catch {
488
- // Skip unreadable plans
521
+ } catch (error) {
522
+ // Expected: file not readable
523
+ if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
524
+ console.error(`Unexpected error processing plan:`, error.message);
525
+ }
489
526
  }
490
527
  }
491
528
 
@@ -526,8 +563,11 @@ async function promoteDecisions(projectPath, decisions, options = {}) {
526
563
  try {
527
564
  await fs.access(filePath);
528
565
  continue;
529
- } catch {
530
- // Safe to create
566
+ } catch (error) {
567
+ // Expected: file doesn't exist, safe to create
568
+ if (error.code !== 'ENOENT') {
569
+ console.error(`Unexpected error checking ${filePath}:`, error.message);
570
+ }
531
571
  }
532
572
 
533
573
  const content = [
@@ -8,7 +8,7 @@
8
8
  * mindmeld harvest # Capture patterns from git history
9
9
  * mindmeld logout # Clear stored authentication
10
10
  *
11
- * @equilateral_ai/mindmeld v3.1.0
11
+ * @equilateral_ai/mindmeld v3.2.0
12
12
  */
13
13
 
14
14
  const fs = require('fs').promises;
@@ -55,7 +55,11 @@ function loadAuth() {
55
55
  try {
56
56
  const data = fsSync.readFileSync(CONFIG.authFile, 'utf-8');
57
57
  return JSON.parse(data);
58
- } catch {
58
+ } catch (error) {
59
+ // Expected: auth file doesn't exist yet
60
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
61
+ console.error('Unexpected error loading auth:', error.message);
62
+ }
59
63
  return null;
60
64
  }
61
65
  }
@@ -75,8 +79,11 @@ async function saveAuth(auth) {
75
79
  async function clearAuth() {
76
80
  try {
77
81
  await fs.unlink(CONFIG.authFile);
78
- } catch {
79
- // File doesn't exist, that's fine
82
+ } catch (error) {
83
+ // Expected: file doesn't exist
84
+ if (error.code !== 'ENOENT') {
85
+ console.error('Unexpected error clearing auth:', error.message);
86
+ }
80
87
  }
81
88
  }
82
89
 
@@ -301,7 +308,11 @@ async function openBrowser(url) {
301
308
  try {
302
309
  await execAsync(command);
303
310
  return true;
304
- } catch {
311
+ } catch (error) {
312
+ // Expected: browser command not available on some systems
313
+ if (error.code !== 'ENOENT' && !error.message.includes('spawn')) {
314
+ console.error('Unexpected error opening browser:', error.message);
315
+ }
305
316
  return false;
306
317
  }
307
318
  }
@@ -419,7 +430,11 @@ function callApi(method, endpoint, token, body = null) {
419
430
  } else {
420
431
  resolve({ success: false, error: parsed.message || parsed.error || 'API error', status: res.statusCode });
421
432
  }
422
- } catch {
433
+ } catch (error) {
434
+ // Expected: API returned non-JSON response
435
+ if (!(error instanceof SyntaxError)) {
436
+ console.error('Unexpected error parsing API response:', error.message);
437
+ }
423
438
  resolve({ success: false, error: data, status: res.statusCode });
424
439
  }
425
440
  });
@@ -538,7 +553,15 @@ async function initProject(projectPath) {
538
553
 
539
554
  console.log('\nšŸŽÆ MindMeld CLI\n');
540
555
 
541
- // 1. Authenticate
556
+ // 1. Analyze repository first (shows value immediately)
557
+ console.log('šŸ“Š Analyzing your repository...\n');
558
+ const { RepoAnalyzer } = require('./repo-analyzer');
559
+ const analyzer = new RepoAnalyzer(projectPath);
560
+ const analysis = await analyzer.analyze({ verbose: false });
561
+ console.log(analyzer.getSummary());
562
+
563
+ // 2. Authenticate
564
+ console.log('\n');
542
565
  const auth = await ensureAuth();
543
566
  console.log(`āœ… Authenticated as ${auth.email}\n`);
544
567
 
@@ -604,7 +627,11 @@ async function initProject(projectPath) {
604
627
  console.log('Aborted.');
605
628
  process.exit(0);
606
629
  }
607
- } catch {
630
+ } catch (error) {
631
+ // Expected: config doesn't exist (not initialized)
632
+ if (error.code !== 'ENOENT') {
633
+ console.error('Unexpected error checking config:', error.message);
634
+ }
608
635
  // Not initialized, continue
609
636
  }
610
637
 
@@ -665,7 +692,11 @@ async function initProject(projectPath) {
665
692
  });
666
693
  console.log('');
667
694
  }
668
- } catch {
695
+ } catch (error) {
696
+ // Expected: not a git repo or git not available
697
+ if (!error.message.includes('not a git repository') && error.code !== 'ENOENT') {
698
+ console.error('Unexpected error reading git history:', error.message);
699
+ }
669
700
  console.log('ā„¹ļø No git history found (not a git repo or no commits)\n');
670
701
  }
671
702
 
@@ -680,16 +711,35 @@ async function initProject(projectPath) {
680
711
  subscriptionTier: tier,
681
712
  collaborators,
682
713
  created: new Date().toISOString(),
683
- mindmeldVersion: '3.1.0'
714
+ mindmeldVersion: '3.2.0',
715
+ // Rich analysis results
716
+ techStack: analysis.techStack,
717
+ structure: analysis.structure,
718
+ documentation: analysis.documentation.map(d => d.relativePath),
719
+ securitySummary: analysis.securityFindings.summary,
720
+ recommendations: analysis.recommendations
684
721
  };
685
722
 
686
723
  await fs.writeFile(configPath, JSON.stringify(config, null, 2));
687
724
 
725
+ // Save full analysis separately
726
+ const analysisPath = path.join(mindmeldDir, 'analysis.json');
727
+ await fs.writeFile(analysisPath, JSON.stringify(analysis, null, 2));
728
+
688
729
  console.log('āœ… MindMeld initialized!\n');
689
730
  console.log(` Config: ${configPath}`);
731
+ console.log(` Analysis: ${path.join(mindmeldDir, 'analysis.json')}`);
690
732
  console.log(` Project ID: ${config.projectId}`);
691
733
  console.log(` User: ${auth.email}`);
692
734
  console.log(` Plan: ${tier}`);
735
+
736
+ // Show tech-specific standards that will be injected
737
+ if (analysis.recommendations.length > 0) {
738
+ console.log('\nšŸ“¦ Standards packs to be injected:');
739
+ for (const rec of analysis.recommendations.filter(r => r.standardsPack)) {
740
+ console.log(` - ${rec.standardsPack}: ${rec.description}`);
741
+ }
742
+ }
693
743
  console.log('');
694
744
 
695
745
  // 9. Check for community standards
@@ -697,7 +747,11 @@ async function initProject(projectPath) {
697
747
  try {
698
748
  await fs.access(standardsDir);
699
749
  console.log('ā„¹ļø Community standards (.equilateral-standards) available');
700
- } catch {
750
+ } catch (error) {
751
+ // Expected: standards dir doesn't exist
752
+ if (error.code !== 'ENOENT') {
753
+ console.error('Unexpected error checking standards:', error.message);
754
+ }
701
755
  if (tier !== 'free') {
702
756
  try {
703
757
  await execAsync(
@@ -705,7 +759,11 @@ async function initProject(projectPath) {
705
759
  { cwd: projectPath }
706
760
  );
707
761
  console.log('āœ… Cloned community standards repo');
708
- } catch {
762
+ } catch (cloneError) {
763
+ // Expected: git not available or network issues
764
+ if (cloneError.code !== 'ENOENT' && !cloneError.message.includes('Could not resolve')) {
765
+ console.error('Unexpected error cloning standards:', cloneError.message);
766
+ }
709
767
  console.log('ā„¹ļø Community standards not available (clone from:');
710
768
  console.log(' https://github.com/Equilateral-AI/EquilateralAgents-Community-Standards)');
711
769
  }
@@ -792,7 +850,11 @@ async function configureClaudeHooks(projectPath) {
792
850
  try {
793
851
  await fs.access(sessionStartHook);
794
852
  await fs.access(preCompactHook);
795
- } catch {
853
+ } catch (error) {
854
+ // Expected: hooks not found in package
855
+ if (error.code !== 'ENOENT') {
856
+ console.error('Unexpected error checking hooks:', error.message);
857
+ }
796
858
  console.log('āš ļø Hook scripts not found in package. Skipping hook configuration.');
797
859
  console.log(` Expected at: ${packageRoot}/hooks/`);
798
860
  return;
@@ -821,7 +883,11 @@ async function configureClaudeHooks(projectPath) {
821
883
  try {
822
884
  const existing = await fs.readFile(settingsPath, 'utf-8');
823
885
  settings = JSON.parse(existing);
824
- } catch {
886
+ } catch (error) {
887
+ // Expected: settings file doesn't exist yet
888
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
889
+ console.error('Unexpected error reading settings:', error.message);
890
+ }
825
891
  // No existing settings
826
892
  }
827
893
 
package/scripts/inject.js CHANGED
@@ -83,8 +83,11 @@ async function detectProjectContext(projectPath) {
83
83
  }
84
84
  context.frameworks.push(...check.frameworks);
85
85
  context.files.push(check.file);
86
- } catch {
87
- // File doesn't exist
86
+ } catch (error) {
87
+ // Expected: file doesn't exist
88
+ if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
89
+ console.error(`Unexpected error checking ${check.file}:`, error.message);
90
+ }
88
91
  }
89
92
  }
90
93
 
@@ -96,8 +99,11 @@ async function detectProjectContext(projectPath) {
96
99
  if (deps['express'] || deps['fastify']) context.frameworks.push('api');
97
100
  if (deps['@aws-sdk']) context.frameworks.push('aws');
98
101
  if (deps['pg'] || deps['mysql2'] || deps['mongoose']) context.frameworks.push('database');
99
- } catch {
100
- // No package.json or can't parse
102
+ } catch (error) {
103
+ // Expected: no package.json or invalid JSON
104
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
105
+ console.error('Unexpected error reading package.json:', error.message);
106
+ }
101
107
  }
102
108
 
103
109
  return context;
@@ -112,7 +118,11 @@ async function getRelevantStandards(projectPath, context) {
112
118
 
113
119
  try {
114
120
  await fs.access(standardsDir);
115
- } catch {
121
+ } catch (error) {
122
+ // Expected: standards dir doesn't exist
123
+ if (error.code !== 'ENOENT') {
124
+ console.error('Unexpected error accessing standards:', error.message);
125
+ }
116
126
  return [];
117
127
  }
118
128
 
@@ -135,8 +145,11 @@ async function getRelevantStandards(projectPath, context) {
135
145
  path: relativePath,
136
146
  score: 0
137
147
  });
138
- } catch {
139
- // Skip unreadable files
148
+ } catch (error) {
149
+ // Expected: file not readable
150
+ if (error.code !== 'ENOENT' && error.code !== 'EACCES') {
151
+ console.error(`Unexpected error reading ${fullPath}:`, error.message);
152
+ }
140
153
  }
141
154
  }
142
155
  }
@@ -194,7 +207,11 @@ async function loadTeamPatterns(projectPath) {
194
207
  collaborators: config.collaborators || [],
195
208
  team: config.team || false
196
209
  };
197
- } catch {
210
+ } catch (error) {
211
+ // Expected: config doesn't exist or invalid JSON
212
+ if (error.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
213
+ console.error('Unexpected error loading team patterns:', error.message);
214
+ }
198
215
  return {
199
216
  projectName: path.basename(projectPath),
200
217
  collaborators: [],
@@ -360,7 +377,11 @@ async function inject(options = {}) {
360
377
  recentLearning: [],
361
378
  collaborators: projectInfo.collaborators || []
362
379
  });
363
- } catch {
380
+ } catch (error) {
381
+ // Expected: module not found in standalone mode
382
+ if (error.code !== 'MODULE_NOT_FOUND') {
383
+ console.error('Unexpected error with claude format:', error.message);
384
+ }
364
385
  output = formatRaw(standards, projectInfo, context);
365
386
  }
366
387
  break;