@equilateral_ai/mindmeld 3.2.0 → 3.3.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 +4 -4
- package/hooks/README.md +46 -4
- package/hooks/pre-compact.js +87 -1
- package/hooks/session-end.js +292 -0
- package/hooks/session-start.js +292 -23
- package/package.json +4 -2
- package/scripts/auth-login.js +53 -0
- package/scripts/init-project.js +69 -375
- package/src/core/AuthManager.js +498 -0
- package/src/core/CrossReferenceEngine.js +624 -0
- package/src/core/DeprecationScheduler.js +183 -0
- package/src/core/LLMPatternDetector.js +218 -0
- package/src/core/RapportOrchestrator.js +186 -0
- package/src/core/RelevanceDetector.js +32 -2
- package/src/core/StandardLifecycle.js +244 -0
- package/src/core/StandardsIngestion.js +341 -28
- package/src/core/parsers/adrParser.js +479 -0
- package/src/core/parsers/cursorRulesParser.js +564 -0
- package/src/core/parsers/eslintParser.js +439 -0
- package/src/handlers/alerts/alertsAcknowledge.js +4 -3
- package/src/handlers/analytics/activitySummaryGet.js +235 -0
- package/src/handlers/analytics/coachingGet.js +361 -0
- package/src/handlers/analytics/developerScoreGet.js +207 -0
- package/src/handlers/collaborators/collaboratorAdd.js +4 -5
- package/src/handlers/collaborators/collaboratorInvite.js +6 -5
- package/src/handlers/collaborators/collaboratorList.js +3 -3
- package/src/handlers/collaborators/collaboratorRemove.js +5 -4
- package/src/handlers/correlations/correlationsDeveloperGet.js +12 -11
- package/src/handlers/correlations/correlationsGet.js +1 -1
- package/src/handlers/correlations/correlationsProjectGet.js +7 -6
- package/src/handlers/enterprise/enterpriseAuditGet.js +108 -0
- package/src/handlers/enterprise/enterpriseContributorsGet.js +85 -0
- package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +53 -0
- package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +77 -0
- package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +71 -0
- package/src/handlers/enterprise/enterpriseKnowledgeGet.js +87 -0
- package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +122 -0
- package/src/handlers/enterprise/enterpriseOnboardingComplete.js +77 -0
- package/src/handlers/enterprise/enterpriseOnboardingInvite.js +138 -0
- package/src/handlers/enterprise/enterpriseOnboardingSetup.js +89 -0
- package/src/handlers/enterprise/enterpriseOnboardingStatus.js +90 -0
- package/src/handlers/github/githubConnectionStatus.js +1 -1
- package/src/handlers/github/githubDiscoverPatterns.js +264 -5
- package/src/handlers/github/githubOAuthCallback.js +14 -2
- package/src/handlers/github/githubOAuthStart.js +1 -1
- package/src/handlers/github/githubPatternsReview.js +1 -1
- package/src/handlers/github/githubReposList.js +1 -1
- package/src/handlers/helpers/auditLogger.js +201 -0
- package/src/handlers/helpers/index.js +19 -1
- package/src/handlers/helpers/lambdaWrapper.js +1 -1
- package/src/handlers/notifications/sendNotification.js +1 -1
- package/src/handlers/projects/projectCreate.js +28 -1
- package/src/handlers/projects/projectDelete.js +3 -3
- package/src/handlers/projects/projectUpdate.js +4 -5
- package/src/handlers/scheduled/analyzeCorrelations.js +3 -3
- package/src/handlers/scheduled/generateAlerts.js +1 -1
- package/src/handlers/standards/catalogGet.js +185 -0
- package/src/handlers/standards/catalogSync.js +120 -0
- package/src/handlers/standards/projectStandardsGet.js +135 -0
- package/src/handlers/standards/projectStandardsPut.js +131 -0
- package/src/handlers/standards/standardsAuditGet.js +65 -0
- package/src/handlers/standards/standardsParseUpload.js +153 -0
- package/src/handlers/standards/standardsRelevantPost.js +213 -0
- package/src/handlers/standards/standardsTransition.js +64 -0
- package/src/handlers/user/userSplashAck.js +91 -0
- package/src/handlers/user/userSplashGet.js +194 -0
- package/src/handlers/users/userProfilePut.js +77 -0
- package/src/index.js +37 -29
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeprecationScheduler.js - Scheduled Deprecation Management
|
|
3
|
+
*
|
|
4
|
+
* Handles the scheduled deprecation workflow for standards:
|
|
5
|
+
* 1. scheduleDeprecation - Marks a standard as deprecated with a grace period
|
|
6
|
+
* 2. processScheduledDeprecations - Deletes standards past their grace period
|
|
7
|
+
* 3. cancelDeprecation - Restores a deprecated standard back to active
|
|
8
|
+
*
|
|
9
|
+
* Called by scheduled Lambda / EventBridge for automatic cleanup,
|
|
10
|
+
* or directly by handlers for user-initiated actions.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { executeQuery } = require('../handlers/helpers/dbOperations');
|
|
14
|
+
const { StandardLifecycle, STATES } = require('./StandardLifecycle');
|
|
15
|
+
|
|
16
|
+
class DeprecationScheduler {
|
|
17
|
+
constructor(config = {}) {
|
|
18
|
+
this.config = {
|
|
19
|
+
defaultGracePeriodDays: config.defaultGracePeriodDays || 30,
|
|
20
|
+
...config
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
this.lifecycle = config.lifecycle || new StandardLifecycle();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Schedule a standard for deprecation with a grace period
|
|
28
|
+
*
|
|
29
|
+
* Sets deprecated_at and deprecation_reason on the standard,
|
|
30
|
+
* transitions state to 'deprecated', and records in audit trail.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} standardId - The standard identifier
|
|
33
|
+
* @param {number} gracePeriodDays - Days before automatic deletion
|
|
34
|
+
* @param {string} userId - Email of the user scheduling deprecation
|
|
35
|
+
* @param {string} reason - Reason for deprecation
|
|
36
|
+
* @returns {Promise<Object>} Deprecation result
|
|
37
|
+
*/
|
|
38
|
+
async scheduleDeprecation(standardId, gracePeriodDays, userId, reason) {
|
|
39
|
+
const days = gracePeriodDays || this.config.defaultGracePeriodDays;
|
|
40
|
+
|
|
41
|
+
// Execute the lifecycle transition to deprecated
|
|
42
|
+
const transitionResult = await this.lifecycle.transition(
|
|
43
|
+
standardId,
|
|
44
|
+
'deprecate',
|
|
45
|
+
userId,
|
|
46
|
+
reason
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Set deprecation metadata on the standard
|
|
50
|
+
const deprecatedAt = new Date();
|
|
51
|
+
const deletionDate = new Date(deprecatedAt.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
52
|
+
|
|
53
|
+
await executeQuery(`
|
|
54
|
+
UPDATE rapport.patterns
|
|
55
|
+
SET
|
|
56
|
+
deprecated_at = $2,
|
|
57
|
+
deprecation_reason = $3
|
|
58
|
+
WHERE pattern_id = $1
|
|
59
|
+
`, [standardId, deprecatedAt, reason]);
|
|
60
|
+
|
|
61
|
+
console.log(
|
|
62
|
+
`[DeprecationScheduler] Scheduled ${standardId} for deletion on ${deletionDate.toISOString()} ` +
|
|
63
|
+
`(${days} day grace period)`
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
standard_id: standardId,
|
|
68
|
+
old_state: transitionResult.old_state,
|
|
69
|
+
new_state: transitionResult.new_state,
|
|
70
|
+
deprecated_at: deprecatedAt.toISOString(),
|
|
71
|
+
grace_period_days: days,
|
|
72
|
+
scheduled_deletion: deletionDate.toISOString(),
|
|
73
|
+
reason: reason,
|
|
74
|
+
audit_entry: transitionResult.audit_entry
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Process all standards that have passed their grace period
|
|
80
|
+
*
|
|
81
|
+
* Finds deprecated standards where deprecated_at + grace period < now,
|
|
82
|
+
* and transitions them to 'deleted'.
|
|
83
|
+
*
|
|
84
|
+
* Intended to be called by a scheduled Lambda / EventBridge rule.
|
|
85
|
+
*
|
|
86
|
+
* @returns {Promise<Object>} Processing results
|
|
87
|
+
*/
|
|
88
|
+
async processScheduledDeprecations() {
|
|
89
|
+
const gracePeriodDays = this.config.defaultGracePeriodDays;
|
|
90
|
+
|
|
91
|
+
// Find standards past their grace period
|
|
92
|
+
const result = await executeQuery(`
|
|
93
|
+
SELECT pattern_id, deprecated_at, deprecation_reason
|
|
94
|
+
FROM rapport.patterns
|
|
95
|
+
WHERE lifecycle_state = $1
|
|
96
|
+
AND deprecated_at IS NOT NULL
|
|
97
|
+
AND deprecated_at + ($2 || ' days')::INTERVAL < NOW()
|
|
98
|
+
`, [STATES.DEPRECATED, gracePeriodDays]);
|
|
99
|
+
|
|
100
|
+
const standards = result.rows;
|
|
101
|
+
|
|
102
|
+
console.log(`[DeprecationScheduler] Found ${standards.length} standards past grace period`);
|
|
103
|
+
|
|
104
|
+
const processed = [];
|
|
105
|
+
const errors = [];
|
|
106
|
+
|
|
107
|
+
for (const standard of standards) {
|
|
108
|
+
try {
|
|
109
|
+
const transitionResult = await this.lifecycle.transition(
|
|
110
|
+
standard.pattern_id,
|
|
111
|
+
'delete',
|
|
112
|
+
'system@mindmeld.dev',
|
|
113
|
+
`Automatic deletion after ${gracePeriodDays}-day grace period. ` +
|
|
114
|
+
`Original reason: ${standard.deprecation_reason || 'Not specified'}`
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
processed.push({
|
|
118
|
+
standard_id: standard.pattern_id,
|
|
119
|
+
deprecated_at: standard.deprecated_at,
|
|
120
|
+
deleted_at: new Date().toISOString()
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
console.log(`[DeprecationScheduler] Deleted ${standard.pattern_id} (deprecated ${standard.deprecated_at})`);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(`[DeprecationScheduler] Failed to delete ${standard.pattern_id}:`, error.message);
|
|
126
|
+
errors.push({
|
|
127
|
+
standard_id: standard.pattern_id,
|
|
128
|
+
error: error.message
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
total_found: standards.length,
|
|
135
|
+
processed: processed.length,
|
|
136
|
+
errors: errors.length,
|
|
137
|
+
deleted: processed,
|
|
138
|
+
failed: errors
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Cancel a scheduled deprecation and restore to active
|
|
144
|
+
*
|
|
145
|
+
* Transitions the standard back to 'active', clears deprecated_at
|
|
146
|
+
* and deprecation_reason, and records in audit trail.
|
|
147
|
+
*
|
|
148
|
+
* @param {string} standardId - The standard identifier
|
|
149
|
+
* @param {string} userId - Email of the user cancelling deprecation
|
|
150
|
+
* @param {string} reason - Reason for cancelling deprecation
|
|
151
|
+
* @returns {Promise<Object>} Cancellation result
|
|
152
|
+
*/
|
|
153
|
+
async cancelDeprecation(standardId, userId, reason) {
|
|
154
|
+
// Execute the lifecycle transition back to active
|
|
155
|
+
const transitionResult = await this.lifecycle.transition(
|
|
156
|
+
standardId,
|
|
157
|
+
'cancel_deprecation',
|
|
158
|
+
userId,
|
|
159
|
+
reason
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
// Clear deprecation metadata
|
|
163
|
+
await executeQuery(`
|
|
164
|
+
UPDATE rapport.patterns
|
|
165
|
+
SET
|
|
166
|
+
deprecated_at = NULL,
|
|
167
|
+
deprecation_reason = NULL
|
|
168
|
+
WHERE pattern_id = $1
|
|
169
|
+
`, [standardId]);
|
|
170
|
+
|
|
171
|
+
console.log(`[DeprecationScheduler] Cancelled deprecation for ${standardId} by ${userId}`);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
standard_id: standardId,
|
|
175
|
+
old_state: transitionResult.old_state,
|
|
176
|
+
new_state: transitionResult.new_state,
|
|
177
|
+
reason: reason,
|
|
178
|
+
audit_entry: transitionResult.audit_entry
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = { DeprecationScheduler };
|
|
@@ -503,6 +503,224 @@ Respond with a JSON object: { "summary": "...", "keyDecisions": ["..."], "newPat
|
|
|
503
503
|
clearCache() {
|
|
504
504
|
this.cache.clear();
|
|
505
505
|
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Analyze repository code samples for patterns (used in GitHub discovery)
|
|
509
|
+
* Returns patterns in YAML standards format with action types
|
|
510
|
+
*
|
|
511
|
+
* @param {string} codeContext - Combined code samples with file paths
|
|
512
|
+
* @param {Object} context - Additional context (tech stack, frameworks)
|
|
513
|
+
* @returns {Promise<Object>} Patterns in YAML-compatible format
|
|
514
|
+
*/
|
|
515
|
+
async analyzeRepoPatterns(codeContext, context = {}) {
|
|
516
|
+
if (!this.isAvailable()) {
|
|
517
|
+
return this.fallbackRepoAnalysis(codeContext, context);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
const systemPrompt = this.buildRepoAnalysisSystemPrompt(context);
|
|
521
|
+
const userPrompt = this.buildRepoAnalysisUserPrompt(codeContext, context);
|
|
522
|
+
|
|
523
|
+
try {
|
|
524
|
+
const requestBody = {
|
|
525
|
+
anthropic_version: 'bedrock-2023-05-31',
|
|
526
|
+
max_tokens: this.config.maxTokens,
|
|
527
|
+
system: systemPrompt,
|
|
528
|
+
messages: [{ role: 'user', content: userPrompt }]
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const command = new InvokeModelCommand({
|
|
532
|
+
modelId: this.config.model,
|
|
533
|
+
contentType: 'application/json',
|
|
534
|
+
accept: 'application/json',
|
|
535
|
+
body: JSON.stringify(requestBody)
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
const response = await this.client.send(command);
|
|
539
|
+
const responseBody = JSON.parse(new TextDecoder().decode(response.body));
|
|
540
|
+
|
|
541
|
+
return this.parseRepoAnalysisResponse(responseBody.content?.[0]?.text || '');
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.error('[LLMPatternDetector] Repo analysis error:', error.message);
|
|
544
|
+
return this.fallbackRepoAnalysis(codeContext, context);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Build system prompt for repository pattern analysis
|
|
550
|
+
*/
|
|
551
|
+
buildRepoAnalysisSystemPrompt(context) {
|
|
552
|
+
const techStack = context.techStack?.join(', ') || 'Unknown';
|
|
553
|
+
const frameworks = context.frameworks?.join(', ') || 'Unknown';
|
|
554
|
+
|
|
555
|
+
return `You are an expert code analyst for MindMeld, a standards and pattern management platform.
|
|
556
|
+
|
|
557
|
+
Your task is to analyze code samples from a repository and extract coding patterns, conventions, and potential issues in a format compatible with our YAML standards schema.
|
|
558
|
+
|
|
559
|
+
Context about this repository:
|
|
560
|
+
- Detected tech stack: ${techStack}
|
|
561
|
+
- Detected frameworks: ${frameworks}
|
|
562
|
+
|
|
563
|
+
Extract patterns using these action types:
|
|
564
|
+
- ALWAYS: Rules that must always be followed (critical patterns)
|
|
565
|
+
- NEVER: Anti-patterns that must be avoided
|
|
566
|
+
- USE: Recommended tools, libraries, or approaches
|
|
567
|
+
- PREFER: Better alternatives when possible
|
|
568
|
+
- AVOID: Patterns to avoid when better alternatives exist
|
|
569
|
+
|
|
570
|
+
Respond ONLY with valid JSON in this exact format:
|
|
571
|
+
{
|
|
572
|
+
"patterns": [
|
|
573
|
+
{
|
|
574
|
+
"element": "Short pattern name",
|
|
575
|
+
"action": "ALWAYS|NEVER|USE|PREFER|AVOID",
|
|
576
|
+
"rule": "Clear description of the rule",
|
|
577
|
+
"category": "api|database|security|performance|architecture|testing|error-handling",
|
|
578
|
+
"confidence": 0.95,
|
|
579
|
+
"evidence": "Code snippet or reference showing this pattern"
|
|
580
|
+
}
|
|
581
|
+
],
|
|
582
|
+
"anti_patterns": [
|
|
583
|
+
{
|
|
584
|
+
"pattern": "Description of the anti-pattern",
|
|
585
|
+
"risk": "Why this is problematic",
|
|
586
|
+
"fix": "How to fix it"
|
|
587
|
+
}
|
|
588
|
+
],
|
|
589
|
+
"recommendations": [
|
|
590
|
+
{
|
|
591
|
+
"category": "standards-category-name",
|
|
592
|
+
"reason": "Why this standards category is relevant",
|
|
593
|
+
"priority": "high|medium|low"
|
|
594
|
+
}
|
|
595
|
+
],
|
|
596
|
+
"tech_summary": {
|
|
597
|
+
"primary_language": "JavaScript/TypeScript/Python/etc",
|
|
598
|
+
"architecture": "serverless|monolith|microservices|frontend-spa",
|
|
599
|
+
"key_patterns": ["pattern1", "pattern2"]
|
|
600
|
+
}
|
|
601
|
+
}`;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Build user prompt for repository analysis
|
|
606
|
+
*/
|
|
607
|
+
buildRepoAnalysisUserPrompt(codeContext, context) {
|
|
608
|
+
const repoName = context.repoName || 'Unknown repository';
|
|
609
|
+
|
|
610
|
+
return `Analyze these code samples from "${repoName}" and extract patterns:
|
|
611
|
+
|
|
612
|
+
${codeContext}
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
Focus on identifying:
|
|
617
|
+
1. Database connection patterns (pooling vs single client, connection reuse)
|
|
618
|
+
2. Error handling approaches (try/catch, wrapper functions, error responses)
|
|
619
|
+
3. API design patterns (response formats, authentication, CORS)
|
|
620
|
+
4. Security patterns (input validation, secrets management, authorization)
|
|
621
|
+
5. Code organization (helper functions, modular structure)
|
|
622
|
+
6. Testing patterns (test frameworks, mocking approaches)
|
|
623
|
+
|
|
624
|
+
Extract actionable patterns that could become team standards.
|
|
625
|
+
Respond with JSON only.`;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Parse repository analysis response
|
|
630
|
+
*/
|
|
631
|
+
parseRepoAnalysisResponse(content) {
|
|
632
|
+
try {
|
|
633
|
+
let jsonStr = content;
|
|
634
|
+
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
635
|
+
if (jsonMatch) {
|
|
636
|
+
jsonStr = jsonMatch[1];
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const parsed = JSON.parse(jsonStr.trim());
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
success: true,
|
|
643
|
+
source: 'llm_repo_analysis',
|
|
644
|
+
model: this.config.model,
|
|
645
|
+
timestamp: new Date().toISOString(),
|
|
646
|
+
patterns: parsed.patterns || [],
|
|
647
|
+
anti_patterns: parsed.anti_patterns || [],
|
|
648
|
+
recommendations: parsed.recommendations || [],
|
|
649
|
+
tech_summary: parsed.tech_summary || {}
|
|
650
|
+
};
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.error('[LLMPatternDetector] Failed to parse repo analysis:', error.message);
|
|
653
|
+
return {
|
|
654
|
+
success: false,
|
|
655
|
+
source: 'llm_repo_analysis',
|
|
656
|
+
error: 'Failed to parse LLM response',
|
|
657
|
+
rawContent: content.substring(0, 500),
|
|
658
|
+
patterns: [],
|
|
659
|
+
anti_patterns: [],
|
|
660
|
+
recommendations: []
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Fallback analysis for repository patterns when LLM unavailable
|
|
667
|
+
*/
|
|
668
|
+
fallbackRepoAnalysis(codeContext, context) {
|
|
669
|
+
const patterns = [];
|
|
670
|
+
const antiPatterns = [];
|
|
671
|
+
|
|
672
|
+
// Basic regex detection for common patterns
|
|
673
|
+
if (/wrapHandler/.test(codeContext)) {
|
|
674
|
+
patterns.push({
|
|
675
|
+
element: 'Lambda Handler Wrapper',
|
|
676
|
+
action: 'USE',
|
|
677
|
+
rule: 'Use wrapHandler for consistent Lambda error handling',
|
|
678
|
+
category: 'api',
|
|
679
|
+
confidence: 0.8,
|
|
680
|
+
evidence: 'Found wrapHandler usage'
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (/executeQuery/.test(codeContext)) {
|
|
685
|
+
patterns.push({
|
|
686
|
+
element: 'Database Query Helper',
|
|
687
|
+
action: 'USE',
|
|
688
|
+
rule: 'Use executeQuery for standardized database operations',
|
|
689
|
+
category: 'database',
|
|
690
|
+
confidence: 0.8,
|
|
691
|
+
evidence: 'Found executeQuery usage'
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (/new Pool\(|createPool/.test(codeContext)) {
|
|
696
|
+
antiPatterns.push({
|
|
697
|
+
pattern: 'Connection pool in Lambda',
|
|
698
|
+
risk: 'Connection pools are wasteful in Lambda - connections persist across invocations',
|
|
699
|
+
fix: 'Use a cached single client instead of a connection pool'
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (/try\s*\{[\s\S]*catch\s*\(/.test(codeContext)) {
|
|
704
|
+
patterns.push({
|
|
705
|
+
element: 'Try-Catch Error Handling',
|
|
706
|
+
action: 'ALWAYS',
|
|
707
|
+
rule: 'Wrap async operations in try-catch blocks',
|
|
708
|
+
category: 'error-handling',
|
|
709
|
+
confidence: 0.7,
|
|
710
|
+
evidence: 'Found try-catch blocks'
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return {
|
|
715
|
+
success: true,
|
|
716
|
+
source: 'fallback_repo_analysis',
|
|
717
|
+
timestamp: new Date().toISOString(),
|
|
718
|
+
patterns,
|
|
719
|
+
anti_patterns: antiPatterns,
|
|
720
|
+
recommendations: [],
|
|
721
|
+
tech_summary: { primary_language: 'Unknown', architecture: 'Unknown', key_patterns: [] }
|
|
722
|
+
};
|
|
723
|
+
}
|
|
506
724
|
}
|
|
507
725
|
|
|
508
726
|
module.exports = { LLMPatternDetector };
|
|
@@ -394,6 +394,192 @@ class RapportOrchestrator extends EventEmitter {
|
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
396
|
|
|
397
|
+
// =============================================
|
|
398
|
+
// Roadmap Team Dispatch (TeamOrchestrator)
|
|
399
|
+
// =============================================
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Roadmap team templates — each team gets isolated agents and phased workflow
|
|
403
|
+
*/
|
|
404
|
+
getRoadmapTeams() {
|
|
405
|
+
return {
|
|
406
|
+
'standards-parsers': {
|
|
407
|
+
description: 'Standards parsers for onboarding upload (ADR, ESLint, Cursor rules)',
|
|
408
|
+
agents: ['CodeGenerationAgent', 'PatternHarvestingAgent'],
|
|
409
|
+
phases: ['ADR_PARSER', 'ESLINT_PARSER', 'CURSOR_RULES_PARSER', 'CROSS_REFERENCE']
|
|
410
|
+
},
|
|
411
|
+
'frontend-curation': {
|
|
412
|
+
description: 'Frontend curation dashboard (review queue, rejection flow, conflict resolution)',
|
|
413
|
+
agents: ['UIUXSpecialistAgent', 'CodeGenerationAgent'],
|
|
414
|
+
phases: ['REVIEW_QUEUE', 'REJECTION_FLOW', 'CONFLICT_RESOLUTION', 'STANDARD_EDITOR']
|
|
415
|
+
},
|
|
416
|
+
'standard-lifecycle': {
|
|
417
|
+
description: 'Standard lifecycle state machine, audit trail, deprecation',
|
|
418
|
+
agents: ['DatabaseAgent', 'AuditorAgent'],
|
|
419
|
+
phases: ['STATE_MACHINE', 'AUDIT_TRAIL_TABLE', 'DEPRECATION_SCHEDULER']
|
|
420
|
+
},
|
|
421
|
+
'weekly-splash-cli': {
|
|
422
|
+
description: 'Weekly splash screen, activity summary, /mm slash commands',
|
|
423
|
+
agents: ['CodeGenerationAgent', 'KnowledgeSynthesisAgent'],
|
|
424
|
+
phases: ['SPLASH_SCREEN', 'ACTIVITY_SUMMARY', 'SLASH_COMMANDS']
|
|
425
|
+
},
|
|
426
|
+
'developer-insights': {
|
|
427
|
+
description: 'Enterprise developer insights (quality scoring, trends, coaching)',
|
|
428
|
+
agents: ['BusinessIntelligenceAgent', 'DataGovernanceAgent'],
|
|
429
|
+
phases: ['QUALITY_SCORING', 'TEAM_TRENDS', 'COACHING_ENGINE']
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Dispatch all roadmap teams in parallel
|
|
436
|
+
* Each team runs with isolated context, dedicated agents, phased workflow
|
|
437
|
+
*
|
|
438
|
+
* @param {Object} options - Dispatch options
|
|
439
|
+
* @param {string} options.team - Dispatch a single team by name (optional)
|
|
440
|
+
* @returns {Promise<Object>} Results from all teams
|
|
441
|
+
*/
|
|
442
|
+
async dispatchRoadmap(options = {}) {
|
|
443
|
+
const allTeams = this.getRoadmapTeams();
|
|
444
|
+
const teamsToRun = options.team
|
|
445
|
+
? { [options.team]: allTeams[options.team] }
|
|
446
|
+
: allTeams;
|
|
447
|
+
|
|
448
|
+
if (options.team && !allTeams[options.team]) {
|
|
449
|
+
throw new Error(`Unknown team: ${options.team}. Available: ${Object.keys(allTeams).join(', ')}`);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
console.log(`[RapportOrchestrator] Dispatching ${Object.keys(teamsToRun).length} roadmap teams in parallel...`);
|
|
453
|
+
this.emit('roadmap:started', { teams: Object.keys(teamsToRun), startedAt: new Date().toISOString() });
|
|
454
|
+
|
|
455
|
+
// Connect to extended agent set for roadmap work
|
|
456
|
+
await this.connectRoadmapAgents(teamsToRun);
|
|
457
|
+
|
|
458
|
+
// Dispatch all teams in parallel
|
|
459
|
+
const teamPromises = Object.entries(teamsToRun).map(([teamName, template]) =>
|
|
460
|
+
this.dispatchTeam(teamName, template, options)
|
|
461
|
+
.then(result => ({ team: teamName, status: 'fulfilled', result }))
|
|
462
|
+
.catch(error => ({ team: teamName, status: 'rejected', error: error.message }))
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
const results = await Promise.all(teamPromises);
|
|
466
|
+
|
|
467
|
+
const summary = {
|
|
468
|
+
total: results.length,
|
|
469
|
+
fulfilled: results.filter(r => r.status === 'fulfilled').length,
|
|
470
|
+
rejected: results.filter(r => r.status === 'rejected').length,
|
|
471
|
+
teams: results,
|
|
472
|
+
completedAt: new Date().toISOString()
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
console.log(`[RapportOrchestrator] Roadmap dispatch complete: ${summary.fulfilled}/${summary.total} teams succeeded`);
|
|
476
|
+
this.emit('roadmap:completed', summary);
|
|
477
|
+
|
|
478
|
+
return summary;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Dispatch a single team with isolated context
|
|
483
|
+
*/
|
|
484
|
+
async dispatchTeam(teamName, template, options = {}) {
|
|
485
|
+
const teamId = `team_${teamName}_${Date.now()}`;
|
|
486
|
+
console.log(`[RapportOrchestrator] Dispatching team: ${teamName} (${teamId})`);
|
|
487
|
+
this.emit('team:started', { teamId, teamName, phases: template.phases });
|
|
488
|
+
|
|
489
|
+
const teamContext = {
|
|
490
|
+
teamId,
|
|
491
|
+
teamName,
|
|
492
|
+
startedAt: new Date().toISOString(),
|
|
493
|
+
projectRoot: this.config.rapportRoot,
|
|
494
|
+
standardsPath: path.join(this.config.rapportRoot, this.config.standardsPath),
|
|
495
|
+
phaseResults: {}
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// Execute phases sequentially within team
|
|
499
|
+
for (const phase of template.phases) {
|
|
500
|
+
console.log(` [${teamName}] Phase: ${phase}`);
|
|
501
|
+
this.emit('phase:started', { teamId, teamName, phase });
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
const phaseResult = await this.executeTeamPhase(teamName, phase, template.agents, teamContext);
|
|
505
|
+
teamContext.phaseResults[phase] = { success: true, result: phaseResult };
|
|
506
|
+
this.emit('phase:completed', { teamId, teamName, phase, result: phaseResult });
|
|
507
|
+
} catch (error) {
|
|
508
|
+
console.error(` [${teamName}] Phase ${phase} failed:`, error.message);
|
|
509
|
+
teamContext.phaseResults[phase] = { success: false, error: error.message };
|
|
510
|
+
this.emit('phase:failed', { teamId, teamName, phase, error: error.message });
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
teamContext.completedAt = new Date().toISOString();
|
|
515
|
+
this.emit('team:completed', { teamId, teamName, context: teamContext });
|
|
516
|
+
|
|
517
|
+
return teamContext;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Execute a single phase within a team, using assigned agents
|
|
522
|
+
*/
|
|
523
|
+
async executeTeamPhase(teamName, phase, agentNames, teamContext) {
|
|
524
|
+
// Try each assigned agent for this phase
|
|
525
|
+
for (const agentName of agentNames) {
|
|
526
|
+
if (!this.agents.has(agentName)) continue;
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
const agent = this.getAgent(agentName);
|
|
530
|
+
|
|
531
|
+
// Try phase-specific method first, then generic execute
|
|
532
|
+
const methodName = `execute${phase.replace(/_/g, '')}`;
|
|
533
|
+
if (typeof agent[methodName] === 'function') {
|
|
534
|
+
return await agent[methodName](teamContext);
|
|
535
|
+
} else if (typeof agent.execute === 'function') {
|
|
536
|
+
return await agent.execute({
|
|
537
|
+
task: phase,
|
|
538
|
+
team: teamName,
|
|
539
|
+
context: teamContext
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
} catch (error) {
|
|
543
|
+
console.warn(` [${teamName}] Agent ${agentName} failed on ${phase}:`, error.message);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Fallback: emit phase for external handling
|
|
548
|
+
return { status: 'pending_implementation', phase, team: teamName };
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Connect additional agents needed for roadmap work
|
|
553
|
+
*/
|
|
554
|
+
async connectRoadmapAgents(teams) {
|
|
555
|
+
const agentsPath = path.join(this.config.equilateralAgentsPath, 'src/agents/specialists');
|
|
556
|
+
const neededAgents = new Set();
|
|
557
|
+
|
|
558
|
+
for (const template of Object.values(teams)) {
|
|
559
|
+
for (const agentName of template.agents) {
|
|
560
|
+
if (!this.agents.has(agentName)) {
|
|
561
|
+
neededAgents.add(agentName);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
for (const agentName of neededAgents) {
|
|
567
|
+
try {
|
|
568
|
+
const agentPath = path.join(agentsPath, `${agentName}.js`);
|
|
569
|
+
const AgentClass = require(agentPath);
|
|
570
|
+
this.agents.set(agentName, {
|
|
571
|
+
name: agentName,
|
|
572
|
+
class: AgentClass,
|
|
573
|
+
instance: null,
|
|
574
|
+
status: 'registered'
|
|
575
|
+
});
|
|
576
|
+
console.log(`[RapportOrchestrator] Registered roadmap agent: ${agentName}`);
|
|
577
|
+
} catch (error) {
|
|
578
|
+
console.warn(`[RapportOrchestrator] Could not load roadmap agent ${agentName}:`, error.message);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
397
583
|
/**
|
|
398
584
|
* Get orchestrator status
|
|
399
585
|
*/
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
const fs = require('fs').promises;
|
|
16
16
|
const path = require('path');
|
|
17
17
|
const { executeQuery } = require('../handlers/helpers/dbOperations');
|
|
18
|
+
const { StandardsIngestion } = require('./StandardsIngestion');
|
|
18
19
|
|
|
19
20
|
class RelevanceDetector {
|
|
20
21
|
constructor(options = {}) {
|
|
@@ -459,8 +460,37 @@ class RelevanceDetector {
|
|
|
459
460
|
const result = await executeQuery(query, [categories]);
|
|
460
461
|
return result.rows;
|
|
461
462
|
} catch (error) {
|
|
462
|
-
//
|
|
463
|
-
console.warn('Standards patterns table not found.
|
|
463
|
+
// Database unavailable — fall back to reading YAML files directly
|
|
464
|
+
console.warn('Standards patterns table not found. Falling back to file-based standards.');
|
|
465
|
+
return this.loadStandardsFromFiles(categories);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* File-system fallback: load and filter standards from YAML files
|
|
471
|
+
* Used when database is unavailable (local dev, first run, DB down)
|
|
472
|
+
*/
|
|
473
|
+
async loadStandardsFromFiles(categories) {
|
|
474
|
+
try {
|
|
475
|
+
const standardsPath = path.isAbsolute(this.standardsPath)
|
|
476
|
+
? this.standardsPath
|
|
477
|
+
: path.join(this.workingDirectory, this.standardsPath);
|
|
478
|
+
|
|
479
|
+
const ingestion = new StandardsIngestion({ standardsPath });
|
|
480
|
+
const allPatterns = await ingestion.parseAllStandards();
|
|
481
|
+
|
|
482
|
+
// Filter by requested categories and enforced/validated maturity
|
|
483
|
+
const validMaturity = ['enforced', 'validated', 'recommended'];
|
|
484
|
+
const categorySet = new Set(categories);
|
|
485
|
+
|
|
486
|
+
return allPatterns
|
|
487
|
+
.filter(p => categorySet.has(p.category) && validMaturity.includes(p.maturity))
|
|
488
|
+
.sort((a, b) => {
|
|
489
|
+
const maturityOrder = { enforced: 1, validated: 2, recommended: 3 };
|
|
490
|
+
return (maturityOrder[a.maturity] || 3) - (maturityOrder[b.maturity] || 3);
|
|
491
|
+
});
|
|
492
|
+
} catch (fallbackError) {
|
|
493
|
+
console.error('File-based standards fallback failed:', fallbackError.message);
|
|
464
494
|
return [];
|
|
465
495
|
}
|
|
466
496
|
}
|