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