@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.
Files changed (139) hide show
  1. package/hooks/session-start.js +312 -85
  2. package/package.json +20 -14
  3. package/scripts/init-project.js +9 -23
  4. package/src/client/dbShim.js +16 -0
  5. package/src/core/AuthManager.js +3 -2
  6. package/src/handlers/helpers/dbOperations.js +9 -46
  7. package/src/index.js +2 -217
  8. package/src/utils/piiMask.js +16 -0
  9. package/scripts/harvest.js +0 -601
  10. package/scripts/inject.js +0 -409
  11. package/scripts/mcp-bridge.js +0 -220
  12. package/scripts/repo-analyzer.js +0 -870
  13. package/scripts/standards.js +0 -285
  14. package/src/collaboration/CollaborationPrompt.js +0 -460
  15. package/src/core/AlertEngine.js +0 -813
  16. package/src/core/AlertNotifier.js +0 -363
  17. package/src/core/CorrelationAnalyzer.js +0 -931
  18. package/src/core/CrossReferenceEngine.js +0 -624
  19. package/src/core/CurationEngine.js +0 -688
  20. package/src/core/DeprecationScheduler.js +0 -183
  21. package/src/core/LoadBearingDetector.js +0 -242
  22. package/src/core/NotificationService.js +0 -1032
  23. package/src/core/RapportOrchestrator.js +0 -632
  24. package/src/core/RelevanceDetector.js +0 -694
  25. package/src/core/StandardLifecycle.js +0 -244
  26. package/src/core/StandardsIngestion.js +0 -991
  27. package/src/core/TeamLoadBearingDetector.js +0 -431
  28. package/src/core/parsers/adrParser.js +0 -479
  29. package/src/core/parsers/cursorRulesParser.js +0 -564
  30. package/src/core/parsers/eslintParser.js +0 -439
  31. package/src/database/dbOperations.js +0 -105
  32. package/src/handlers/activity/activityGetMe.js +0 -98
  33. package/src/handlers/activity/activityGetTeam.js +0 -175
  34. package/src/handlers/admin/adminSetup.js +0 -216
  35. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  36. package/src/handlers/alerts/alertsGet.js +0 -250
  37. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  38. package/src/handlers/analytics/coachingGet.js +0 -361
  39. package/src/handlers/analytics/convergenceGet.js +0 -236
  40. package/src/handlers/analytics/developerScoreGet.js +0 -137
  41. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  42. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  43. package/src/handlers/collaborators/collaboratorList.js +0 -82
  44. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  45. package/src/handlers/collaborators/inviteAccept.js +0 -122
  46. package/src/handlers/company/companyUsersDelete.js +0 -141
  47. package/src/handlers/company/companyUsersGet.js +0 -90
  48. package/src/handlers/company/companyUsersPost.js +0 -267
  49. package/src/handlers/company/companyUsersPut.js +0 -76
  50. package/src/handlers/context/contextGet.js +0 -57
  51. package/src/handlers/context/invariantsGet.js +0 -74
  52. package/src/handlers/context/loopsGet.js +0 -82
  53. package/src/handlers/context/notesCreate.js +0 -74
  54. package/src/handlers/context/purposeGet.js +0 -78
  55. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  56. package/src/handlers/correlations/correlationsGet.js +0 -93
  57. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  58. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  59. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  60. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  61. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  62. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  63. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  64. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  65. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  66. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  67. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  68. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  69. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  70. package/src/handlers/github/githubConnectionStatus.js +0 -49
  71. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  72. package/src/handlers/github/githubOAuthCallback.js +0 -178
  73. package/src/handlers/github/githubOAuthStart.js +0 -59
  74. package/src/handlers/github/githubPatternsReview.js +0 -76
  75. package/src/handlers/github/githubReposList.js +0 -105
  76. package/src/handlers/health/healthGet.js +0 -55
  77. package/src/handlers/helpers/auditLogger.js +0 -201
  78. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  79. package/src/handlers/helpers/decisionFrames.js +0 -29
  80. package/src/handlers/helpers/errorHandler.js +0 -49
  81. package/src/handlers/helpers/index.js +0 -138
  82. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  83. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  84. package/src/handlers/helpers/predictiveCache.js +0 -51
  85. package/src/handlers/helpers/projectAccess.js +0 -88
  86. package/src/handlers/helpers/responseUtil.js +0 -55
  87. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  88. package/src/handlers/mcp/mcpHandler.js +0 -569
  89. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  90. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  91. package/src/handlers/notifications/getPreferences.js +0 -84
  92. package/src/handlers/notifications/sendNotification.js +0 -170
  93. package/src/handlers/notifications/updatePreferences.js +0 -316
  94. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  95. package/src/handlers/patterns/patternUsagePost.js +0 -182
  96. package/src/handlers/patterns/patternViolationPost.js +0 -185
  97. package/src/handlers/projects/projectCreate.js +0 -248
  98. package/src/handlers/projects/projectDelete.js +0 -82
  99. package/src/handlers/projects/projectGet.js +0 -95
  100. package/src/handlers/projects/projectUpdate.js +0 -117
  101. package/src/handlers/reports/aiLeverage.js +0 -210
  102. package/src/handlers/reports/engineeringInvestment.js +0 -132
  103. package/src/handlers/reports/riskForecast.js +0 -206
  104. package/src/handlers/reports/standardsRoi.js +0 -254
  105. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  106. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  107. package/src/handlers/scheduled/generateAlerts.js +0 -135
  108. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  109. package/src/handlers/scheduled/refreshActivity.js +0 -21
  110. package/src/handlers/scheduled/scanCompliance.js +0 -334
  111. package/src/handlers/sessions/sessionEndPost.js +0 -180
  112. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  113. package/src/handlers/standards/catalogGet.js +0 -185
  114. package/src/handlers/standards/catalogSync.js +0 -120
  115. package/src/handlers/standards/discoveriesGet.js +0 -89
  116. package/src/handlers/standards/projectStandardsGet.js +0 -129
  117. package/src/handlers/standards/projectStandardsPut.js +0 -151
  118. package/src/handlers/standards/standardsAuditGet.js +0 -65
  119. package/src/handlers/standards/standardsParseUpload.js +0 -149
  120. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  121. package/src/handlers/standards/standardsTransition.js +0 -161
  122. package/src/handlers/stripe/addonManagePost.js +0 -240
  123. package/src/handlers/stripe/billingPortalPost.js +0 -93
  124. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  125. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  126. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  127. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  128. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  129. package/src/handlers/stripe/webhookPost.js +0 -482
  130. package/src/handlers/user/apiTokenCreate.js +0 -71
  131. package/src/handlers/user/apiTokenList.js +0 -64
  132. package/src/handlers/user/userSplashAck.js +0 -91
  133. package/src/handlers/user/userSplashGet.js +0 -211
  134. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  135. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  136. package/src/handlers/users/userEntitlementsGet.js +0 -89
  137. package/src/handlers/users/userGet.js +0 -118
  138. package/src/handlers/users/userProfilePut.js +0 -77
  139. 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 };