@equilateral_ai/mindmeld 3.3.0 → 3.4.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.
- package/README.md +1 -10
- package/hooks/pre-compact.js +213 -25
- package/hooks/session-start.js +636 -42
- package/hooks/subagent-start.js +150 -0
- package/hooks/subagent-stop.js +184 -0
- package/package.json +8 -7
- package/scripts/init-project.js +74 -33
- package/scripts/mcp-bridge.js +220 -0
- package/src/core/CorrelationAnalyzer.js +157 -0
- package/src/core/LLMPatternDetector.js +198 -0
- package/src/core/RelevanceDetector.js +123 -36
- package/src/core/StandardsIngestion.js +119 -18
- package/src/handlers/activity/activityGetMe.js +1 -1
- package/src/handlers/activity/activityGetTeam.js +100 -55
- package/src/handlers/admin/adminSetup.js +216 -0
- package/src/handlers/alerts/alertsAcknowledge.js +6 -6
- package/src/handlers/alerts/alertsGet.js +11 -11
- package/src/handlers/analytics/activitySummaryGet.js +34 -35
- package/src/handlers/analytics/coachingGet.js +11 -11
- package/src/handlers/analytics/convergenceGet.js +236 -0
- package/src/handlers/analytics/developerScoreGet.js +41 -111
- package/src/handlers/collaborators/collaboratorInvite.js +1 -1
- package/src/handlers/company/companyUsersDelete.js +141 -0
- package/src/handlers/company/companyUsersGet.js +90 -0
- package/src/handlers/company/companyUsersPost.js +267 -0
- package/src/handlers/company/companyUsersPut.js +76 -0
- package/src/handlers/correlations/correlationsDeveloperGet.js +12 -12
- package/src/handlers/correlations/correlationsGet.js +8 -8
- package/src/handlers/correlations/correlationsProjectGet.js +5 -5
- package/src/handlers/enterprise/controlTowerGet.js +224 -0
- package/src/handlers/enterprise/enterpriseOnboardingSetup.js +48 -9
- package/src/handlers/enterprise/enterpriseOnboardingStatus.js +1 -3
- package/src/handlers/github/githubConnectionStatus.js +1 -1
- package/src/handlers/github/githubDiscoverPatterns.js +4 -2
- package/src/handlers/github/githubPatternsReview.js +7 -36
- package/src/handlers/health/healthGet.js +55 -0
- package/src/handlers/helpers/checkSuperAdmin.js +13 -14
- package/src/handlers/helpers/subscriptionTiers.js +27 -27
- package/src/handlers/mcp/mcpHandler.js +569 -0
- package/src/handlers/mcp/mindmeldMcpHandler.js +689 -0
- package/src/handlers/notifications/sendNotification.js +18 -18
- package/src/handlers/patterns/patternEvaluatePromotionPost.js +173 -0
- package/src/handlers/projects/projectCreate.js +124 -10
- package/src/handlers/projects/projectDelete.js +4 -4
- package/src/handlers/projects/projectGet.js +8 -8
- package/src/handlers/projects/projectUpdate.js +4 -4
- package/src/handlers/reports/aiLeverage.js +34 -30
- package/src/handlers/reports/engineeringInvestment.js +16 -16
- package/src/handlers/reports/riskForecast.js +41 -21
- package/src/handlers/reports/standardsRoi.js +101 -9
- package/src/handlers/scheduled/maturityUpdateJob.js +166 -0
- package/src/handlers/sessions/sessionStandardsPost.js +43 -7
- package/src/handlers/standards/discoveriesGet.js +93 -0
- package/src/handlers/standards/projectStandardsGet.js +2 -2
- package/src/handlers/standards/projectStandardsPut.js +2 -2
- package/src/handlers/standards/standardsRelevantPost.js +107 -12
- package/src/handlers/standards/standardsTransition.js +112 -15
- package/src/handlers/stripe/billingPortalPost.js +1 -1
- package/src/handlers/stripe/enterpriseCheckoutPost.js +2 -2
- package/src/handlers/stripe/subscriptionCreatePost.js +2 -2
- package/src/handlers/stripe/webhookPost.js +42 -14
- package/src/handlers/user/apiTokenCreate.js +71 -0
- package/src/handlers/user/apiTokenList.js +64 -0
- package/src/handlers/user/userSplashGet.js +90 -73
- package/src/handlers/users/cognitoPostConfirmation.js +37 -1
- package/src/handlers/users/cognitoPreSignUp.js +114 -0
- package/src/handlers/users/userGet.js +12 -8
- package/src/handlers/webhooks/githubWebhook.js +117 -125
- package/src/index.js +46 -51
package/README.md
CHANGED
|
@@ -209,11 +209,6 @@ npm run test:hooks
|
|
|
209
209
|
npm run test:session-start
|
|
210
210
|
npm run test:pre-compact
|
|
211
211
|
|
|
212
|
-
# Test standards parsing (no database)
|
|
213
|
-
node scripts/demo-standards-parsing.js
|
|
214
|
-
|
|
215
|
-
# Test standards ingestion (requires database)
|
|
216
|
-
node scripts/test-standards-ingestion.js
|
|
217
212
|
```
|
|
218
213
|
|
|
219
214
|
### Prerequisites
|
|
@@ -246,11 +241,7 @@ Rapport integrates with `.equilateral-standards/` to provide context-aware stand
|
|
|
246
241
|
|
|
247
242
|
**Test it**:
|
|
248
243
|
```bash
|
|
249
|
-
|
|
250
|
-
node scripts/demo-standards-parsing.js
|
|
251
|
-
|
|
252
|
-
# Full ingestion test (requires database)
|
|
253
|
-
node scripts/test-standards-ingestion.js
|
|
244
|
+
node scripts/ingest-standards.js
|
|
254
245
|
```
|
|
255
246
|
|
|
256
247
|
## Value Proposition
|
package/hooks/pre-compact.js
CHANGED
|
@@ -66,10 +66,27 @@ async function loadAuthToken() {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* Load Cognito config
|
|
69
|
+
* Load Cognito config
|
|
70
|
+
* Priority: .mindmeld/config.json → .myworld.json → production defaults
|
|
70
71
|
* @returns {Promise<Object>} Cognito constructor options or empty object
|
|
71
72
|
*/
|
|
72
73
|
async function loadCognitoConfig() {
|
|
74
|
+
// 1. Check .mindmeld/config.json first (always points to prod)
|
|
75
|
+
try {
|
|
76
|
+
const mindmeldConfigPath = path.join(process.cwd(), '.mindmeld', 'config.json');
|
|
77
|
+
const content = await fs.readFile(mindmeldConfigPath, 'utf-8');
|
|
78
|
+
const config = JSON.parse(content);
|
|
79
|
+
if (config.auth?.cognitoDomain && config.auth?.cognitoClientId) {
|
|
80
|
+
return {
|
|
81
|
+
cognitoDomain: config.auth.cognitoDomain,
|
|
82
|
+
cognitoClientId: config.auth.cognitoClientId
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// No .mindmeld/config.json or no auth section
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 2. Fallback to .myworld.json (may point to dev)
|
|
73
90
|
try {
|
|
74
91
|
const configPath = path.join(process.cwd(), '.myworld.json');
|
|
75
92
|
const content = await fs.readFile(configPath, 'utf-8');
|
|
@@ -88,10 +105,24 @@ async function loadCognitoConfig() {
|
|
|
88
105
|
}
|
|
89
106
|
|
|
90
107
|
/**
|
|
91
|
-
* Load API configuration
|
|
108
|
+
* Load API configuration
|
|
109
|
+
* Priority: .mindmeld/config.json → .myworld.json → env var → production default
|
|
92
110
|
* @returns {Promise<{apiUrl: string}>}
|
|
93
111
|
*/
|
|
94
112
|
async function loadApiConfig() {
|
|
113
|
+
// 1. Check .mindmeld/config.json first (always points to prod)
|
|
114
|
+
try {
|
|
115
|
+
const mindmeldConfigPath = path.join(process.cwd(), '.mindmeld', 'config.json');
|
|
116
|
+
const content = await fs.readFile(mindmeldConfigPath, 'utf-8');
|
|
117
|
+
const config = JSON.parse(content);
|
|
118
|
+
if (config.apiUrl) {
|
|
119
|
+
return { apiUrl: config.apiUrl };
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
// No .mindmeld/config.json or no apiUrl
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 2. Fallback to .myworld.json (may point to dev)
|
|
95
126
|
try {
|
|
96
127
|
const configPath = path.join(process.cwd(), '.myworld.json');
|
|
97
128
|
const content = await fs.readFile(configPath, 'utf-8');
|
|
@@ -134,9 +165,22 @@ async function harvestPatterns(sessionTranscript) {
|
|
|
134
165
|
apiUrl: apiConfig.apiUrl
|
|
135
166
|
});
|
|
136
167
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
168
|
+
// Load project context so reinforcePattern/recordViolation have project_id
|
|
169
|
+
await mindmeld.detectProject();
|
|
170
|
+
|
|
171
|
+
// Load session context persisted by session-start hook
|
|
172
|
+
let sessionContext = null;
|
|
173
|
+
try {
|
|
174
|
+
const sessionContextPath = path.join(process.cwd(), '.mindmeld', 'current-session.json');
|
|
175
|
+
const content = await fs.readFile(sessionContextPath, 'utf-8');
|
|
176
|
+
sessionContext = JSON.parse(content);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
// Expected: file may not exist if session-start didn't run
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Extract session metadata — prefer persisted session context
|
|
182
|
+
const sessionId = sessionContext?.sessionId || sessionTranscript.sessionId || generateSessionId();
|
|
183
|
+
const userId = sessionContext?.userEmail || sessionTranscript.userId || process.env.USER || 'unknown';
|
|
140
184
|
|
|
141
185
|
// 1. Detect patterns from session (LLM-powered or regex fallback)
|
|
142
186
|
let patterns = [];
|
|
@@ -216,27 +260,18 @@ async function harvestPatterns(sessionTranscript) {
|
|
|
216
260
|
// 5. Check for promotion candidates
|
|
217
261
|
const candidates = await checkPromotionCandidates(mindmeld, validationResults.valid);
|
|
218
262
|
|
|
219
|
-
// 6.
|
|
220
|
-
let
|
|
263
|
+
// 6. Harvest plans from ~/.claude/plans/
|
|
264
|
+
let harvestedPlans = [];
|
|
221
265
|
try {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
console.error(`[MindMeld] Changes since last update: ${readmeStatus.changes}`);
|
|
228
|
-
console.error(`[MindMeld] Agent changes: ${readmeStatus.analysis.agentChanges}`);
|
|
229
|
-
console.error(`[MindMeld] Standards changes: ${readmeStatus.analysis.standardsChanges}`);
|
|
230
|
-
console.error(`[MindMeld] Significance: ${readmeStatus.analysis.significance}`);
|
|
231
|
-
|
|
232
|
-
if (readmeStatus.shouldTrigger) {
|
|
233
|
-
console.error(`[MindMeld] 🔧 Critical changes detected - README update recommended`);
|
|
234
|
-
console.error(`[MindMeld] Run: node -e "const L=require('./src/agents/specialists/LibrarianAgent');new L().generateProjectReadme({projectRoot:process.cwd()})"`);
|
|
266
|
+
harvestedPlans = await harvestPlans(sessionTranscript);
|
|
267
|
+
if (harvestedPlans.length > 0) {
|
|
268
|
+
console.error(`[MindMeld] Harvested ${harvestedPlans.length} plan(s) from session`);
|
|
269
|
+
for (const plan of harvestedPlans) {
|
|
270
|
+
console.error(` - ${plan.title} (${plan.filename})`);
|
|
235
271
|
}
|
|
236
272
|
}
|
|
237
273
|
} catch (error) {
|
|
238
|
-
|
|
239
|
-
console.error(`[MindMeld] README check skipped:`, error.message);
|
|
274
|
+
console.error('[MindMeld] Plan harvesting failed (non-fatal):', error.message);
|
|
240
275
|
}
|
|
241
276
|
|
|
242
277
|
// 7. Log results
|
|
@@ -246,8 +281,10 @@ async function harvestPatterns(sessionTranscript) {
|
|
|
246
281
|
violations: validationResults.violations.length,
|
|
247
282
|
reinforced: validationResults.valid.length,
|
|
248
283
|
promotionCandidates: candidates.length,
|
|
249
|
-
|
|
250
|
-
|
|
284
|
+
plansHarvested: harvestedPlans.length,
|
|
285
|
+
plans: harvestedPlans,
|
|
286
|
+
readmeStale: null,
|
|
287
|
+
readmeUpdateRecommended: false,
|
|
251
288
|
llmUsed: useLLM && llmAnalysis?.success,
|
|
252
289
|
llmModel: llmAnalysis?.model || null,
|
|
253
290
|
llmSummary: llmAnalysis?.summary || null,
|
|
@@ -279,6 +316,133 @@ async function harvestPatterns(sessionTranscript) {
|
|
|
279
316
|
}
|
|
280
317
|
}
|
|
281
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Harvest plans from ~/.claude/plans/
|
|
321
|
+
* Scans for plan files modified during this session (within last 4 hours)
|
|
322
|
+
* Extracts title, project references, and key decisions
|
|
323
|
+
*
|
|
324
|
+
* @param {Object} sessionTranscript - Session data (may contain session start time)
|
|
325
|
+
* @returns {Promise<Array>} Array of harvested plan objects
|
|
326
|
+
*/
|
|
327
|
+
async function harvestPlans(sessionTranscript) {
|
|
328
|
+
const os = require('os');
|
|
329
|
+
const plansDir = path.join(os.homedir(), '.claude', 'plans');
|
|
330
|
+
|
|
331
|
+
let files;
|
|
332
|
+
try {
|
|
333
|
+
files = await fs.readdir(plansDir);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
if (error.code === 'ENOENT') return [];
|
|
336
|
+
throw error;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const mdFiles = files.filter(f => f.endsWith('.md'));
|
|
340
|
+
if (mdFiles.length === 0) return [];
|
|
341
|
+
|
|
342
|
+
// Plans modified in the last 4 hours are considered session-relevant
|
|
343
|
+
const cutoff = Date.now() - (4 * 60 * 60 * 1000);
|
|
344
|
+
const harvested = [];
|
|
345
|
+
|
|
346
|
+
for (const filename of mdFiles) {
|
|
347
|
+
const filePath = path.join(plansDir, filename);
|
|
348
|
+
const stat = await fs.stat(filePath);
|
|
349
|
+
|
|
350
|
+
if (stat.mtimeMs < cutoff) continue;
|
|
351
|
+
|
|
352
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
353
|
+
const plan = parsePlanFile(filename, content, stat);
|
|
354
|
+
if (plan) {
|
|
355
|
+
harvested.push(plan);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Sort by most recently modified first
|
|
360
|
+
harvested.sort((a, b) => b.modifiedAt - a.modifiedAt);
|
|
361
|
+
|
|
362
|
+
return harvested;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Parse a plan file into structured data
|
|
367
|
+
* @param {string} filename - Plan filename
|
|
368
|
+
* @param {string} content - Raw markdown content
|
|
369
|
+
* @param {Object} stat - File stat object
|
|
370
|
+
* @returns {Object|null} Parsed plan or null if empty
|
|
371
|
+
*/
|
|
372
|
+
function parsePlanFile(filename, content, stat) {
|
|
373
|
+
if (!content || content.trim().length === 0) return null;
|
|
374
|
+
|
|
375
|
+
const lines = content.split('\n');
|
|
376
|
+
|
|
377
|
+
// Extract title from first heading
|
|
378
|
+
let title = filename.replace('.md', '');
|
|
379
|
+
for (const line of lines) {
|
|
380
|
+
const headingMatch = line.match(/^#\s+(?:Plan:\s*)?(.+)/);
|
|
381
|
+
if (headingMatch) {
|
|
382
|
+
title = headingMatch[1].trim();
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Extract sections
|
|
388
|
+
const sections = {};
|
|
389
|
+
let currentSection = null;
|
|
390
|
+
let currentContent = [];
|
|
391
|
+
|
|
392
|
+
for (const line of lines) {
|
|
393
|
+
const sectionMatch = line.match(/^##\s+(.+)/);
|
|
394
|
+
if (sectionMatch) {
|
|
395
|
+
if (currentSection) {
|
|
396
|
+
sections[currentSection] = currentContent.join('\n').trim();
|
|
397
|
+
}
|
|
398
|
+
currentSection = sectionMatch[1].trim().toLowerCase();
|
|
399
|
+
currentContent = [];
|
|
400
|
+
} else if (currentSection) {
|
|
401
|
+
currentContent.push(line);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (currentSection) {
|
|
405
|
+
sections[currentSection] = currentContent.join('\n').trim();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Extract file paths mentioned (src/..., hooks/..., etc.)
|
|
409
|
+
const fileRefs = [];
|
|
410
|
+
const filePattern = /(?:^|\s|`)((?:src|hooks|scripts|frontend|lib|test|tests)\/[\w/./-]+\.\w+)/g;
|
|
411
|
+
let match;
|
|
412
|
+
while ((match = filePattern.exec(content)) !== null) {
|
|
413
|
+
if (!fileRefs.includes(match[1])) {
|
|
414
|
+
fileRefs.push(match[1]);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Detect which project this plan relates to
|
|
419
|
+
const projectHints = [];
|
|
420
|
+
const projectPatterns = [
|
|
421
|
+
/rapport/i, /mindmeld/i, /equilateral/i, /jarvis/i,
|
|
422
|
+
/honeydo/i, /timebridge/i, /powerspec/i
|
|
423
|
+
];
|
|
424
|
+
for (const pp of projectPatterns) {
|
|
425
|
+
if (pp.test(content)) {
|
|
426
|
+
projectHints.push(pp.source.replace(/\/i$/, ''));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
filename: filename,
|
|
432
|
+
planId: filename.replace('.md', ''),
|
|
433
|
+
title: title,
|
|
434
|
+
modifiedAt: stat.mtimeMs,
|
|
435
|
+
modifiedIso: stat.mtime.toISOString(),
|
|
436
|
+
sizeBytes: stat.size,
|
|
437
|
+
lineCount: lines.length,
|
|
438
|
+
sections: Object.keys(sections),
|
|
439
|
+
context: sections['context'] || null,
|
|
440
|
+
filesReferenced: fileRefs.slice(0, 20),
|
|
441
|
+
projectHints: projectHints,
|
|
442
|
+
content: content
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
282
446
|
/**
|
|
283
447
|
* Check if MindMeld is configured for this project
|
|
284
448
|
*/
|
|
@@ -405,6 +569,30 @@ async function generatePostCompactContext(summary, llmAnalysis) {
|
|
|
405
569
|
sections.push('');
|
|
406
570
|
}
|
|
407
571
|
|
|
572
|
+
// Include active plans from this session
|
|
573
|
+
if (summary.plans && summary.plans.length > 0) {
|
|
574
|
+
sections.push('## Active Plans');
|
|
575
|
+
for (const plan of summary.plans) {
|
|
576
|
+
sections.push(`### ${plan.title}`);
|
|
577
|
+
sections.push(`- **File**: \`~/.claude/plans/${plan.filename}\``);
|
|
578
|
+
sections.push(`- **Modified**: ${plan.modifiedIso}`);
|
|
579
|
+
if (plan.projectHints.length > 0) {
|
|
580
|
+
sections.push(`- **Projects**: ${plan.projectHints.join(', ')}`);
|
|
581
|
+
}
|
|
582
|
+
if (plan.filesReferenced.length > 0) {
|
|
583
|
+
sections.push(`- **Files**: ${plan.filesReferenced.slice(0, 10).join(', ')}`);
|
|
584
|
+
}
|
|
585
|
+
if (plan.context) {
|
|
586
|
+
// Include first 500 chars of context section
|
|
587
|
+
const contextPreview = plan.context.length > 500
|
|
588
|
+
? plan.context.substring(0, 500) + '...'
|
|
589
|
+
: plan.context;
|
|
590
|
+
sections.push(`- **Context**: ${contextPreview}`);
|
|
591
|
+
}
|
|
592
|
+
sections.push('');
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
408
596
|
// Load relevant standards from .equilateral-standards if available
|
|
409
597
|
const standardsPath = path.join(process.cwd(), '.equilateral-standards');
|
|
410
598
|
try {
|
|
@@ -492,4 +680,4 @@ if (require.main === module) {
|
|
|
492
680
|
});
|
|
493
681
|
}
|
|
494
682
|
|
|
495
|
-
module.exports = { harvestPatterns, parseSessionTranscript, generatePostCompactContext };
|
|
683
|
+
module.exports = { harvestPatterns, harvestPlans, parseSessionTranscript, generatePostCompactContext };
|