@equilateral_ai/mindmeld 3.5.3 → 4.0.1
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/hooks/session-start.js +312 -85
- package/package.json +20 -14
- package/scripts/init-project.js +9 -23
- package/src/client/dbShim.js +16 -0
- package/src/core/AuthManager.js +3 -2
- package/src/handlers/helpers/dbOperations.js +9 -46
- package/src/index.js +2 -217
- package/src/utils/piiMask.js +16 -0
- package/scripts/harvest.js +0 -601
- package/scripts/inject.js +0 -409
- package/scripts/mcp-bridge.js +0 -220
- package/scripts/repo-analyzer.js +0 -870
- package/scripts/standards.js +0 -285
- package/src/collaboration/CollaborationPrompt.js +0 -460
- package/src/core/AlertEngine.js +0 -813
- package/src/core/AlertNotifier.js +0 -363
- package/src/core/CorrelationAnalyzer.js +0 -931
- package/src/core/CrossReferenceEngine.js +0 -624
- package/src/core/CurationEngine.js +0 -688
- package/src/core/DeprecationScheduler.js +0 -183
- package/src/core/LoadBearingDetector.js +0 -242
- package/src/core/NotificationService.js +0 -1032
- package/src/core/RapportOrchestrator.js +0 -632
- package/src/core/RelevanceDetector.js +0 -694
- package/src/core/StandardLifecycle.js +0 -244
- package/src/core/StandardsIngestion.js +0 -991
- package/src/core/TeamLoadBearingDetector.js +0 -431
- package/src/core/parsers/adrParser.js +0 -479
- package/src/core/parsers/cursorRulesParser.js +0 -564
- package/src/core/parsers/eslintParser.js +0 -439
- package/src/database/dbOperations.js +0 -105
- package/src/handlers/activity/activityGetMe.js +0 -98
- package/src/handlers/activity/activityGetTeam.js +0 -175
- package/src/handlers/admin/adminSetup.js +0 -216
- package/src/handlers/alerts/alertsAcknowledge.js +0 -92
- package/src/handlers/alerts/alertsGet.js +0 -250
- package/src/handlers/analytics/activitySummaryGet.js +0 -234
- package/src/handlers/analytics/coachingGet.js +0 -361
- package/src/handlers/analytics/convergenceGet.js +0 -236
- package/src/handlers/analytics/developerScoreGet.js +0 -137
- package/src/handlers/collaborators/collaboratorAdd.js +0 -200
- package/src/handlers/collaborators/collaboratorInvite.js +0 -219
- package/src/handlers/collaborators/collaboratorList.js +0 -82
- package/src/handlers/collaborators/collaboratorRemove.js +0 -128
- package/src/handlers/collaborators/inviteAccept.js +0 -122
- package/src/handlers/company/companyUsersDelete.js +0 -141
- package/src/handlers/company/companyUsersGet.js +0 -90
- package/src/handlers/company/companyUsersPost.js +0 -267
- package/src/handlers/company/companyUsersPut.js +0 -76
- package/src/handlers/context/contextGet.js +0 -57
- package/src/handlers/context/invariantsGet.js +0 -74
- package/src/handlers/context/loopsGet.js +0 -82
- package/src/handlers/context/notesCreate.js +0 -74
- package/src/handlers/context/purposeGet.js +0 -78
- package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
- package/src/handlers/correlations/correlationsGet.js +0 -93
- package/src/handlers/correlations/correlationsProjectGet.js +0 -153
- package/src/handlers/enterprise/controlTowerGet.js +0 -224
- package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
- package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
- package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
- package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
- package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
- package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
- package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
- package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
- package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
- package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
- package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
- package/src/handlers/github/githubConnectionStatus.js +0 -49
- package/src/handlers/github/githubDiscoverPatterns.js +0 -621
- package/src/handlers/github/githubOAuthCallback.js +0 -178
- package/src/handlers/github/githubOAuthStart.js +0 -59
- package/src/handlers/github/githubPatternsReview.js +0 -76
- package/src/handlers/github/githubReposList.js +0 -105
- package/src/handlers/health/healthGet.js +0 -55
- package/src/handlers/helpers/auditLogger.js +0 -201
- package/src/handlers/helpers/checkSuperAdmin.js +0 -84
- package/src/handlers/helpers/decisionFrames.js +0 -29
- package/src/handlers/helpers/errorHandler.js +0 -49
- package/src/handlers/helpers/index.js +0 -138
- package/src/handlers/helpers/lambdaWrapper.js +0 -60
- package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
- package/src/handlers/helpers/predictiveCache.js +0 -51
- package/src/handlers/helpers/projectAccess.js +0 -88
- package/src/handlers/helpers/responseUtil.js +0 -55
- package/src/handlers/helpers/subscriptionTiers.js +0 -1168
- package/src/handlers/mcp/mcpHandler.js +0 -569
- package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
- package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
- package/src/handlers/notifications/getPreferences.js +0 -84
- package/src/handlers/notifications/sendNotification.js +0 -170
- package/src/handlers/notifications/updatePreferences.js +0 -316
- package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
- package/src/handlers/patterns/patternUsagePost.js +0 -182
- package/src/handlers/patterns/patternViolationPost.js +0 -185
- package/src/handlers/projects/projectCreate.js +0 -248
- package/src/handlers/projects/projectDelete.js +0 -82
- package/src/handlers/projects/projectGet.js +0 -95
- package/src/handlers/projects/projectUpdate.js +0 -117
- package/src/handlers/reports/aiLeverage.js +0 -210
- package/src/handlers/reports/engineeringInvestment.js +0 -132
- package/src/handlers/reports/riskForecast.js +0 -206
- package/src/handlers/reports/standardsRoi.js +0 -254
- package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
- package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
- package/src/handlers/scheduled/generateAlerts.js +0 -135
- package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
- package/src/handlers/scheduled/refreshActivity.js +0 -21
- package/src/handlers/scheduled/scanCompliance.js +0 -334
- package/src/handlers/sessions/sessionEndPost.js +0 -180
- package/src/handlers/sessions/sessionStandardsPost.js +0 -171
- package/src/handlers/standards/catalogGet.js +0 -185
- package/src/handlers/standards/catalogSync.js +0 -120
- package/src/handlers/standards/discoveriesGet.js +0 -89
- package/src/handlers/standards/projectStandardsGet.js +0 -129
- package/src/handlers/standards/projectStandardsPut.js +0 -151
- package/src/handlers/standards/standardsAuditGet.js +0 -65
- package/src/handlers/standards/standardsParseUpload.js +0 -149
- package/src/handlers/standards/standardsRelevantPost.js +0 -405
- package/src/handlers/standards/standardsTransition.js +0 -161
- package/src/handlers/stripe/addonManagePost.js +0 -240
- package/src/handlers/stripe/billingPortalPost.js +0 -93
- package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
- package/src/handlers/stripe/seatsUpdatePost.js +0 -185
- package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
- package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
- package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
- package/src/handlers/stripe/webhookPost.js +0 -482
- package/src/handlers/user/apiTokenCreate.js +0 -71
- package/src/handlers/user/apiTokenList.js +0 -64
- package/src/handlers/user/userSplashAck.js +0 -91
- package/src/handlers/user/userSplashGet.js +0 -211
- package/src/handlers/users/cognitoPostConfirmation.js +0 -186
- package/src/handlers/users/cognitoPreSignUp.js +0 -114
- package/src/handlers/users/userEntitlementsGet.js +0 -89
- package/src/handlers/users/userGet.js +0 -118
- package/src/handlers/users/userProfilePut.js +0 -77
- package/src/handlers/webhooks/githubWebhook.js +0 -215
|
@@ -1,624 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CrossReferenceEngine - Compare uploaded vs discovered standards
|
|
3
|
-
*
|
|
4
|
-
* Analyzes overlap, conflicts, and new discoveries between standards
|
|
5
|
-
* that were explicitly uploaded by a team and those discovered through
|
|
6
|
-
* codebase analysis or pattern detection.
|
|
7
|
-
*
|
|
8
|
-
* Returns:
|
|
9
|
-
* confirmed - Both uploaded and discovered agree on the same rule
|
|
10
|
-
* conflicts - Uploaded says one thing, discovered shows the opposite
|
|
11
|
-
* newDiscoveries - Found in discovered but not present in uploaded
|
|
12
|
-
*
|
|
13
|
-
* Each result item includes a confidence score (0.0 - 1.0)
|
|
14
|
-
*
|
|
15
|
-
* @module CrossReferenceEngine
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Action types considered opposite to each other
|
|
20
|
-
*/
|
|
21
|
-
const OPPOSING_ACTIONS = {
|
|
22
|
-
'ALWAYS': 'NEVER',
|
|
23
|
-
'NEVER': 'ALWAYS',
|
|
24
|
-
'USE': 'AVOID',
|
|
25
|
-
'AVOID': 'USE',
|
|
26
|
-
'PREFER': 'AVOID'
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Action types considered aligned with each other
|
|
31
|
-
*/
|
|
32
|
-
const ALIGNED_ACTIONS = {
|
|
33
|
-
'ALWAYS': ['ALWAYS', 'USE', 'PREFER'],
|
|
34
|
-
'NEVER': ['NEVER', 'AVOID'],
|
|
35
|
-
'USE': ['USE', 'ALWAYS', 'PREFER'],
|
|
36
|
-
'PREFER': ['PREFER', 'USE', 'ALWAYS'],
|
|
37
|
-
'AVOID': ['AVOID', 'NEVER']
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
class CrossReferenceEngine {
|
|
41
|
-
constructor(options = {}) {
|
|
42
|
-
this.similarityThreshold = options.similarityThreshold || 0.4;
|
|
43
|
-
this.highConfidenceThreshold = options.highConfidenceThreshold || 0.8;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Cross-reference uploaded standards against discovered standards
|
|
48
|
-
*
|
|
49
|
-
* @param {Array<Object>} uploaded - Standards explicitly uploaded by the team
|
|
50
|
-
* Each item: { id, category, rules: [{ action, rule }], anti_patterns, tags }
|
|
51
|
-
* @param {Array<Object>} discovered - Standards discovered from codebase analysis
|
|
52
|
-
* Each item: { id, category, rules: [{ action, rule }], anti_patterns, tags }
|
|
53
|
-
* @returns {Object} Cross-reference results
|
|
54
|
-
* { confirmed: [...], conflicts: [...], newDiscoveries: [...] }
|
|
55
|
-
*/
|
|
56
|
-
crossReference(uploaded, discovered) {
|
|
57
|
-
if (!Array.isArray(uploaded) || !Array.isArray(discovered)) {
|
|
58
|
-
throw new Error('Both uploaded and discovered must be arrays');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const confirmed = [];
|
|
62
|
-
const conflicts = [];
|
|
63
|
-
const newDiscoveries = [];
|
|
64
|
-
|
|
65
|
-
// Flatten all rules from uploaded standards for comparison
|
|
66
|
-
const uploadedRules = this.flattenRules(uploaded);
|
|
67
|
-
const discoveredRules = this.flattenRules(discovered);
|
|
68
|
-
|
|
69
|
-
// Track which discovered rules have been matched
|
|
70
|
-
const matchedDiscoveredIndices = new Set();
|
|
71
|
-
|
|
72
|
-
// Compare each discovered rule against all uploaded rules
|
|
73
|
-
for (let dIdx = 0; dIdx < discoveredRules.length; dIdx++) {
|
|
74
|
-
const discoveredRule = discoveredRules[dIdx];
|
|
75
|
-
let bestMatch = null;
|
|
76
|
-
let bestSimilarity = 0;
|
|
77
|
-
|
|
78
|
-
for (let uIdx = 0; uIdx < uploadedRules.length; uIdx++) {
|
|
79
|
-
const uploadedRule = uploadedRules[uIdx];
|
|
80
|
-
|
|
81
|
-
const similarity = this.computeRuleSimilarity(uploadedRule, discoveredRule);
|
|
82
|
-
|
|
83
|
-
if (similarity > bestSimilarity && similarity >= this.similarityThreshold) {
|
|
84
|
-
bestSimilarity = similarity;
|
|
85
|
-
bestMatch = { uploadedRule, uploadedIndex: uIdx, similarity };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (bestMatch) {
|
|
90
|
-
matchedDiscoveredIndices.add(dIdx);
|
|
91
|
-
|
|
92
|
-
const isAligned = this.areActionsAligned(
|
|
93
|
-
bestMatch.uploadedRule.action,
|
|
94
|
-
discoveredRule.action
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const isConflicting = this.areActionsConflicting(
|
|
98
|
-
bestMatch.uploadedRule.action,
|
|
99
|
-
discoveredRule.action
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
if (isAligned) {
|
|
103
|
-
confirmed.push({
|
|
104
|
-
uploaded: {
|
|
105
|
-
standardId: bestMatch.uploadedRule.standardId,
|
|
106
|
-
action: bestMatch.uploadedRule.action,
|
|
107
|
-
rule: bestMatch.uploadedRule.rule,
|
|
108
|
-
category: bestMatch.uploadedRule.category
|
|
109
|
-
},
|
|
110
|
-
discovered: {
|
|
111
|
-
standardId: discoveredRule.standardId,
|
|
112
|
-
action: discoveredRule.action,
|
|
113
|
-
rule: discoveredRule.rule,
|
|
114
|
-
category: discoveredRule.category
|
|
115
|
-
},
|
|
116
|
-
confidence: this.computeConfirmationConfidence(bestMatch.similarity, bestMatch.uploadedRule, discoveredRule),
|
|
117
|
-
matchType: 'confirmed',
|
|
118
|
-
similarity: bestMatch.similarity
|
|
119
|
-
});
|
|
120
|
-
} else if (isConflicting) {
|
|
121
|
-
conflicts.push({
|
|
122
|
-
uploaded: {
|
|
123
|
-
standardId: bestMatch.uploadedRule.standardId,
|
|
124
|
-
action: bestMatch.uploadedRule.action,
|
|
125
|
-
rule: bestMatch.uploadedRule.rule,
|
|
126
|
-
category: bestMatch.uploadedRule.category
|
|
127
|
-
},
|
|
128
|
-
discovered: {
|
|
129
|
-
standardId: discoveredRule.standardId,
|
|
130
|
-
action: discoveredRule.action,
|
|
131
|
-
rule: discoveredRule.rule,
|
|
132
|
-
category: discoveredRule.category
|
|
133
|
-
},
|
|
134
|
-
confidence: this.computeConflictConfidence(bestMatch.similarity, bestMatch.uploadedRule, discoveredRule),
|
|
135
|
-
matchType: 'conflict',
|
|
136
|
-
similarity: bestMatch.similarity,
|
|
137
|
-
reason: this.describeConflict(bestMatch.uploadedRule, discoveredRule)
|
|
138
|
-
});
|
|
139
|
-
} else {
|
|
140
|
-
// Ambiguous match - related but not clearly aligned or conflicting
|
|
141
|
-
// Treat as confirmed with lower confidence
|
|
142
|
-
confirmed.push({
|
|
143
|
-
uploaded: {
|
|
144
|
-
standardId: bestMatch.uploadedRule.standardId,
|
|
145
|
-
action: bestMatch.uploadedRule.action,
|
|
146
|
-
rule: bestMatch.uploadedRule.rule,
|
|
147
|
-
category: bestMatch.uploadedRule.category
|
|
148
|
-
},
|
|
149
|
-
discovered: {
|
|
150
|
-
standardId: discoveredRule.standardId,
|
|
151
|
-
action: discoveredRule.action,
|
|
152
|
-
rule: discoveredRule.rule,
|
|
153
|
-
category: discoveredRule.category
|
|
154
|
-
},
|
|
155
|
-
confidence: bestMatch.similarity * 0.5,
|
|
156
|
-
matchType: 'partial',
|
|
157
|
-
similarity: bestMatch.similarity
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
} else {
|
|
161
|
-
// No match found - this is a new discovery
|
|
162
|
-
matchedDiscoveredIndices.add(dIdx);
|
|
163
|
-
newDiscoveries.push({
|
|
164
|
-
discovered: {
|
|
165
|
-
standardId: discoveredRule.standardId,
|
|
166
|
-
action: discoveredRule.action,
|
|
167
|
-
rule: discoveredRule.rule,
|
|
168
|
-
category: discoveredRule.category
|
|
169
|
-
},
|
|
170
|
-
confidence: this.computeDiscoveryConfidence(discoveredRule),
|
|
171
|
-
matchType: 'new_discovery'
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Also check anti-patterns cross-references
|
|
177
|
-
const antiPatternResults = this.crossReferenceAntiPatterns(uploaded, discovered);
|
|
178
|
-
confirmed.push(...antiPatternResults.confirmed);
|
|
179
|
-
conflicts.push(...antiPatternResults.conflicts);
|
|
180
|
-
|
|
181
|
-
// Sort each result set by confidence descending
|
|
182
|
-
confirmed.sort((a, b) => b.confidence - a.confidence);
|
|
183
|
-
conflicts.sort((a, b) => b.confidence - a.confidence);
|
|
184
|
-
newDiscoveries.sort((a, b) => b.confidence - a.confidence);
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
confirmed,
|
|
188
|
-
conflicts,
|
|
189
|
-
newDiscoveries,
|
|
190
|
-
summary: {
|
|
191
|
-
totalUploaded: uploadedRules.length,
|
|
192
|
-
totalDiscovered: discoveredRules.length,
|
|
193
|
-
confirmedCount: confirmed.length,
|
|
194
|
-
conflictCount: conflicts.length,
|
|
195
|
-
newDiscoveryCount: newDiscoveries.length,
|
|
196
|
-
overallAlignment: this.computeOverallAlignment(confirmed, conflicts, newDiscoveries)
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Flatten standards objects into individual rule records for comparison
|
|
203
|
-
*
|
|
204
|
-
* @param {Array<Object>} standards - Array of standards objects
|
|
205
|
-
* @returns {Array<Object>} Flattened rule records
|
|
206
|
-
*/
|
|
207
|
-
flattenRules(standards) {
|
|
208
|
-
const flattened = [];
|
|
209
|
-
|
|
210
|
-
for (const standard of standards) {
|
|
211
|
-
const rules = Array.isArray(standard.rules) ? standard.rules : [];
|
|
212
|
-
|
|
213
|
-
for (const rule of rules) {
|
|
214
|
-
flattened.push({
|
|
215
|
-
standardId: standard.id || 'unknown',
|
|
216
|
-
category: standard.category || 'general',
|
|
217
|
-
action: rule.action || 'USE',
|
|
218
|
-
rule: rule.rule || '',
|
|
219
|
-
tags: standard.tags || [],
|
|
220
|
-
priority: standard.priority || 30
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return flattened;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Compute similarity between two rules using text and semantic analysis
|
|
230
|
-
*
|
|
231
|
-
* @param {Object} ruleA - First rule
|
|
232
|
-
* @param {Object} ruleB - Second rule
|
|
233
|
-
* @returns {number} Similarity score (0.0 - 1.0)
|
|
234
|
-
*/
|
|
235
|
-
computeRuleSimilarity(ruleA, ruleB) {
|
|
236
|
-
const textA = ruleA.rule.toLowerCase();
|
|
237
|
-
const textB = ruleB.rule.toLowerCase();
|
|
238
|
-
|
|
239
|
-
// Exact match
|
|
240
|
-
if (textA === textB) return 1.0;
|
|
241
|
-
|
|
242
|
-
// Token-based similarity (Jaccard)
|
|
243
|
-
const tokensA = this.tokenize(textA);
|
|
244
|
-
const tokensB = this.tokenize(textB);
|
|
245
|
-
const jaccardScore = this.jaccardSimilarity(tokensA, tokensB);
|
|
246
|
-
|
|
247
|
-
// Category match bonus
|
|
248
|
-
const categoryBonus = ruleA.category === ruleB.category ? 0.1 : 0;
|
|
249
|
-
|
|
250
|
-
// Tag overlap bonus
|
|
251
|
-
const tagOverlap = this.computeTagOverlap(ruleA.tags, ruleB.tags);
|
|
252
|
-
const tagBonus = tagOverlap * 0.05;
|
|
253
|
-
|
|
254
|
-
// Substring containment bonus
|
|
255
|
-
let containmentBonus = 0;
|
|
256
|
-
if (textA.includes(textB) || textB.includes(textA)) {
|
|
257
|
-
containmentBonus = 0.2;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Key term match (technical terms carry more weight)
|
|
261
|
-
const keyTermScore = this.computeKeyTermOverlap(tokensA, tokensB);
|
|
262
|
-
|
|
263
|
-
// Weighted combination
|
|
264
|
-
const score = Math.min(1.0,
|
|
265
|
-
(jaccardScore * 0.4) +
|
|
266
|
-
(keyTermScore * 0.3) +
|
|
267
|
-
containmentBonus +
|
|
268
|
-
categoryBonus +
|
|
269
|
-
tagBonus
|
|
270
|
-
);
|
|
271
|
-
|
|
272
|
-
return Math.round(score * 100) / 100;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Tokenize text into meaningful words
|
|
277
|
-
*
|
|
278
|
-
* @param {string} text - Text to tokenize
|
|
279
|
-
* @returns {Set<string>} Set of tokens
|
|
280
|
-
*/
|
|
281
|
-
tokenize(text) {
|
|
282
|
-
const stopWords = new Set([
|
|
283
|
-
'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been',
|
|
284
|
-
'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from',
|
|
285
|
-
'and', 'or', 'but', 'not', 'no', 'do', 'does', 'did',
|
|
286
|
-
'this', 'that', 'it', 'its', 'all', 'any', 'each', 'some',
|
|
287
|
-
'should', 'must', 'shall', 'will', 'can', 'may',
|
|
288
|
-
'use', 'using', 'used'
|
|
289
|
-
]);
|
|
290
|
-
|
|
291
|
-
const words = text
|
|
292
|
-
.replace(/[^a-z0-9\s-]/g, ' ')
|
|
293
|
-
.split(/\s+/)
|
|
294
|
-
.filter(w => w.length > 1 && !stopWords.has(w));
|
|
295
|
-
|
|
296
|
-
return new Set(words);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Compute Jaccard similarity between two token sets
|
|
301
|
-
*
|
|
302
|
-
* @param {Set<string>} setA - First token set
|
|
303
|
-
* @param {Set<string>} setB - Second token set
|
|
304
|
-
* @returns {number} Jaccard similarity (0.0 - 1.0)
|
|
305
|
-
*/
|
|
306
|
-
jaccardSimilarity(setA, setB) {
|
|
307
|
-
if (setA.size === 0 && setB.size === 0) return 0;
|
|
308
|
-
|
|
309
|
-
let intersection = 0;
|
|
310
|
-
for (const token of setA) {
|
|
311
|
-
if (setB.has(token)) intersection++;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const union = setA.size + setB.size - intersection;
|
|
315
|
-
return union > 0 ? intersection / union : 0;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Compute overlap of technical key terms between token sets
|
|
320
|
-
*
|
|
321
|
-
* @param {Set<string>} tokensA - First token set
|
|
322
|
-
* @param {Set<string>} tokensB - Second token set
|
|
323
|
-
* @returns {number} Key term overlap score (0.0 - 1.0)
|
|
324
|
-
*/
|
|
325
|
-
computeKeyTermOverlap(tokensA, tokensB) {
|
|
326
|
-
// Technical terms carry more semantic weight
|
|
327
|
-
const technicalTerms = new Set([
|
|
328
|
-
'async', 'await', 'promise', 'callback', 'error', 'exception',
|
|
329
|
-
'variable', 'function', 'class', 'interface', 'type', 'const', 'let', 'var',
|
|
330
|
-
'import', 'export', 'module', 'require', 'dependency',
|
|
331
|
-
'database', 'query', 'sql', 'connection', 'pool', 'transaction',
|
|
332
|
-
'api', 'rest', 'graphql', 'endpoint', 'handler', 'middleware',
|
|
333
|
-
'lambda', 'serverless', 'aws', 'cloud', 'deploy',
|
|
334
|
-
'react', 'component', 'state', 'hook', 'render', 'props',
|
|
335
|
-
'test', 'mock', 'assert', 'coverage', 'jest',
|
|
336
|
-
'security', 'auth', 'token', 'encryption', 'validation',
|
|
337
|
-
'log', 'console', 'debug', 'monitor', 'trace',
|
|
338
|
-
'cache', 'performance', 'optimization', 'memory',
|
|
339
|
-
'unused', 'deprecated', 'legacy'
|
|
340
|
-
]);
|
|
341
|
-
|
|
342
|
-
const keyTermsA = new Set([...tokensA].filter(t => technicalTerms.has(t)));
|
|
343
|
-
const keyTermsB = new Set([...tokensB].filter(t => technicalTerms.has(t)));
|
|
344
|
-
|
|
345
|
-
if (keyTermsA.size === 0 && keyTermsB.size === 0) return 0;
|
|
346
|
-
|
|
347
|
-
return this.jaccardSimilarity(keyTermsA, keyTermsB);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Compute tag overlap between two tag arrays
|
|
352
|
-
*
|
|
353
|
-
* @param {Array<string>} tagsA - First tag set
|
|
354
|
-
* @param {Array<string>} tagsB - Second tag set
|
|
355
|
-
* @returns {number} Tag overlap ratio (0.0 - 1.0)
|
|
356
|
-
*/
|
|
357
|
-
computeTagOverlap(tagsA, tagsB) {
|
|
358
|
-
if (!tagsA || !tagsB || tagsA.length === 0 || tagsB.length === 0) return 0;
|
|
359
|
-
|
|
360
|
-
const setA = new Set(tagsA.map(t => t.toLowerCase()));
|
|
361
|
-
const setB = new Set(tagsB.map(t => t.toLowerCase()));
|
|
362
|
-
|
|
363
|
-
return this.jaccardSimilarity(setA, setB);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
/**
|
|
367
|
-
* Check if two actions are aligned (same direction)
|
|
368
|
-
*
|
|
369
|
-
* @param {string} actionA - First action
|
|
370
|
-
* @param {string} actionB - Second action
|
|
371
|
-
* @returns {boolean} True if actions are aligned
|
|
372
|
-
*/
|
|
373
|
-
areActionsAligned(actionA, actionB) {
|
|
374
|
-
const aligned = ALIGNED_ACTIONS[actionA];
|
|
375
|
-
return aligned ? aligned.includes(actionB) : actionA === actionB;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Check if two actions are conflicting (opposite direction)
|
|
380
|
-
*
|
|
381
|
-
* @param {string} actionA - First action
|
|
382
|
-
* @param {string} actionB - Second action
|
|
383
|
-
* @returns {boolean} True if actions conflict
|
|
384
|
-
*/
|
|
385
|
-
areActionsConflicting(actionA, actionB) {
|
|
386
|
-
return OPPOSING_ACTIONS[actionA] === actionB;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Compute confidence score for a confirmed match
|
|
391
|
-
*
|
|
392
|
-
* @param {number} similarity - Text similarity score
|
|
393
|
-
* @param {Object} uploadedRule - Uploaded rule
|
|
394
|
-
* @param {Object} discoveredRule - Discovered rule
|
|
395
|
-
* @returns {number} Confidence score (0.0 - 1.0)
|
|
396
|
-
*/
|
|
397
|
-
computeConfirmationConfidence(similarity, uploadedRule, discoveredRule) {
|
|
398
|
-
let confidence = similarity;
|
|
399
|
-
|
|
400
|
-
// Exact action match increases confidence
|
|
401
|
-
if (uploadedRule.action === discoveredRule.action) {
|
|
402
|
-
confidence = Math.min(1.0, confidence + 0.15);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Same category increases confidence
|
|
406
|
-
if (uploadedRule.category === discoveredRule.category) {
|
|
407
|
-
confidence = Math.min(1.0, confidence + 0.1);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Higher priority rules get slightly higher confidence
|
|
411
|
-
if (uploadedRule.priority <= 20 || discoveredRule.priority <= 20) {
|
|
412
|
-
confidence = Math.min(1.0, confidence + 0.05);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return Math.round(confidence * 100) / 100;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Compute confidence score for a conflict
|
|
420
|
-
*
|
|
421
|
-
* @param {number} similarity - Text similarity score
|
|
422
|
-
* @param {Object} uploadedRule - Uploaded rule
|
|
423
|
-
* @param {Object} discoveredRule - Discovered rule
|
|
424
|
-
* @returns {number} Confidence score (0.0 - 1.0)
|
|
425
|
-
*/
|
|
426
|
-
computeConflictConfidence(similarity, uploadedRule, discoveredRule) {
|
|
427
|
-
let confidence = similarity;
|
|
428
|
-
|
|
429
|
-
// Direct opposition (ALWAYS vs NEVER) is high confidence conflict
|
|
430
|
-
if (OPPOSING_ACTIONS[uploadedRule.action] === discoveredRule.action) {
|
|
431
|
-
confidence = Math.min(1.0, confidence + 0.2);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Same category makes the conflict more significant
|
|
435
|
-
if (uploadedRule.category === discoveredRule.category) {
|
|
436
|
-
confidence = Math.min(1.0, confidence + 0.1);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return Math.round(confidence * 100) / 100;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
/**
|
|
443
|
-
* Compute confidence score for a new discovery
|
|
444
|
-
*
|
|
445
|
-
* @param {Object} discoveredRule - Discovered rule
|
|
446
|
-
* @returns {number} Confidence score (0.0 - 1.0)
|
|
447
|
-
*/
|
|
448
|
-
computeDiscoveryConfidence(discoveredRule) {
|
|
449
|
-
let confidence = 0.5; // Base confidence for any discovery
|
|
450
|
-
|
|
451
|
-
// Stronger action types indicate higher confidence
|
|
452
|
-
if (discoveredRule.action === 'ALWAYS' || discoveredRule.action === 'NEVER') {
|
|
453
|
-
confidence += 0.2;
|
|
454
|
-
} else if (discoveredRule.action === 'USE' || discoveredRule.action === 'AVOID') {
|
|
455
|
-
confidence += 0.1;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Higher priority discovered rules are more confident
|
|
459
|
-
if (discoveredRule.priority === 10) {
|
|
460
|
-
confidence += 0.1;
|
|
461
|
-
} else if (discoveredRule.priority === 20) {
|
|
462
|
-
confidence += 0.05;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Longer, more descriptive rules indicate more specificity
|
|
466
|
-
if (discoveredRule.rule.length > 50) {
|
|
467
|
-
confidence += 0.1;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return Math.min(1.0, Math.round(confidence * 100) / 100);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Generate a human-readable description of a conflict
|
|
475
|
-
*
|
|
476
|
-
* @param {Object} uploadedRule - Uploaded rule
|
|
477
|
-
* @param {Object} discoveredRule - Discovered rule
|
|
478
|
-
* @returns {string} Conflict description
|
|
479
|
-
*/
|
|
480
|
-
describeConflict(uploadedRule, discoveredRule) {
|
|
481
|
-
return `Uploaded standard says "${uploadedRule.action}: ${uploadedRule.rule}" ` +
|
|
482
|
-
`but discovered pattern shows "${discoveredRule.action}: ${discoveredRule.rule}"`;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Cross-reference anti-patterns between uploaded and discovered standards
|
|
487
|
-
*
|
|
488
|
-
* @param {Array<Object>} uploaded - Uploaded standards
|
|
489
|
-
* @param {Array<Object>} discovered - Discovered standards
|
|
490
|
-
* @returns {Object} Anti-pattern cross-reference results
|
|
491
|
-
*/
|
|
492
|
-
crossReferenceAntiPatterns(uploaded, discovered) {
|
|
493
|
-
const confirmed = [];
|
|
494
|
-
const conflicts = [];
|
|
495
|
-
|
|
496
|
-
const uploadedAntiPatterns = this.flattenAntiPatterns(uploaded);
|
|
497
|
-
const discoveredAntiPatterns = this.flattenAntiPatterns(discovered);
|
|
498
|
-
|
|
499
|
-
for (const discoveredAp of discoveredAntiPatterns) {
|
|
500
|
-
for (const uploadedAp of uploadedAntiPatterns) {
|
|
501
|
-
const similarity = this.computeTextSimilarity(
|
|
502
|
-
uploadedAp.text,
|
|
503
|
-
discoveredAp.text
|
|
504
|
-
);
|
|
505
|
-
|
|
506
|
-
if (similarity >= this.similarityThreshold) {
|
|
507
|
-
confirmed.push({
|
|
508
|
-
uploaded: {
|
|
509
|
-
standardId: uploadedAp.standardId,
|
|
510
|
-
action: 'AVOID',
|
|
511
|
-
rule: uploadedAp.text,
|
|
512
|
-
category: uploadedAp.category
|
|
513
|
-
},
|
|
514
|
-
discovered: {
|
|
515
|
-
standardId: discoveredAp.standardId,
|
|
516
|
-
action: 'AVOID',
|
|
517
|
-
rule: discoveredAp.text,
|
|
518
|
-
category: discoveredAp.category
|
|
519
|
-
},
|
|
520
|
-
confidence: Math.round(similarity * 100) / 100,
|
|
521
|
-
matchType: 'anti_pattern_confirmed',
|
|
522
|
-
similarity
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Check if any uploaded anti-patterns are found as positive rules in discovered
|
|
529
|
-
const discoveredRules = this.flattenRules(discovered);
|
|
530
|
-
for (const uploadedAp of uploadedAntiPatterns) {
|
|
531
|
-
for (const discoveredRule of discoveredRules) {
|
|
532
|
-
if (discoveredRule.action === 'ALWAYS' || discoveredRule.action === 'USE' || discoveredRule.action === 'PREFER') {
|
|
533
|
-
const similarity = this.computeTextSimilarity(
|
|
534
|
-
uploadedAp.text,
|
|
535
|
-
discoveredRule.rule
|
|
536
|
-
);
|
|
537
|
-
|
|
538
|
-
if (similarity >= this.similarityThreshold) {
|
|
539
|
-
conflicts.push({
|
|
540
|
-
uploaded: {
|
|
541
|
-
standardId: uploadedAp.standardId,
|
|
542
|
-
action: 'AVOID',
|
|
543
|
-
rule: uploadedAp.text,
|
|
544
|
-
category: uploadedAp.category
|
|
545
|
-
},
|
|
546
|
-
discovered: {
|
|
547
|
-
standardId: discoveredRule.standardId,
|
|
548
|
-
action: discoveredRule.action,
|
|
549
|
-
rule: discoveredRule.rule,
|
|
550
|
-
category: discoveredRule.category
|
|
551
|
-
},
|
|
552
|
-
confidence: Math.round(similarity * 100) / 100,
|
|
553
|
-
matchType: 'anti_pattern_conflict',
|
|
554
|
-
similarity,
|
|
555
|
-
reason: `Uploaded standard lists "${uploadedAp.text}" as an anti-pattern, ` +
|
|
556
|
-
`but discovered pattern promotes "${discoveredRule.action}: ${discoveredRule.rule}"`
|
|
557
|
-
});
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
return { confirmed, conflicts };
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
/**
|
|
567
|
-
* Flatten anti-patterns from standards objects
|
|
568
|
-
*
|
|
569
|
-
* @param {Array<Object>} standards - Standards objects
|
|
570
|
-
* @returns {Array<Object>} Flattened anti-pattern records
|
|
571
|
-
*/
|
|
572
|
-
flattenAntiPatterns(standards) {
|
|
573
|
-
const flattened = [];
|
|
574
|
-
|
|
575
|
-
for (const standard of standards) {
|
|
576
|
-
const antiPatterns = Array.isArray(standard.anti_patterns) ? standard.anti_patterns : [];
|
|
577
|
-
|
|
578
|
-
for (const ap of antiPatterns) {
|
|
579
|
-
const text = typeof ap === 'string' ? ap : (ap.description || ap.text || '');
|
|
580
|
-
if (text) {
|
|
581
|
-
flattened.push({
|
|
582
|
-
standardId: standard.id || 'unknown',
|
|
583
|
-
category: standard.category || 'general',
|
|
584
|
-
text
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return flattened;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* Compute text similarity between two strings
|
|
595
|
-
*
|
|
596
|
-
* @param {string} textA - First text
|
|
597
|
-
* @param {string} textB - Second text
|
|
598
|
-
* @returns {number} Similarity score (0.0 - 1.0)
|
|
599
|
-
*/
|
|
600
|
-
computeTextSimilarity(textA, textB) {
|
|
601
|
-
const tokensA = this.tokenize(textA.toLowerCase());
|
|
602
|
-
const tokensB = this.tokenize(textB.toLowerCase());
|
|
603
|
-
return this.jaccardSimilarity(tokensA, tokensB);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Compute overall alignment score
|
|
608
|
-
*
|
|
609
|
-
* @param {Array} confirmed - Confirmed matches
|
|
610
|
-
* @param {Array} conflicts - Conflicts found
|
|
611
|
-
* @param {Array} newDiscoveries - New discoveries
|
|
612
|
-
* @returns {number} Overall alignment (0.0 - 1.0)
|
|
613
|
-
*/
|
|
614
|
-
computeOverallAlignment(confirmed, conflicts, newDiscoveries) {
|
|
615
|
-
const total = confirmed.length + conflicts.length + newDiscoveries.length;
|
|
616
|
-
if (total === 0) return 1.0;
|
|
617
|
-
|
|
618
|
-
// Confirmed matches contribute positively, conflicts negatively
|
|
619
|
-
const alignmentScore = (confirmed.length - conflicts.length * 0.5) / total;
|
|
620
|
-
return Math.max(0, Math.min(1.0, Math.round(alignmentScore * 100) / 100));
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
module.exports = { CrossReferenceEngine };
|