agentic-qe 1.3.3 → 1.3.5

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 (147) hide show
  1. package/.claude/agents/qe-api-contract-validator.md +20 -0
  2. package/.claude/agents/qe-chaos-engineer.md +20 -0
  3. package/.claude/agents/qe-coverage-analyzer.md +21 -0
  4. package/.claude/agents/qe-deployment-readiness.md +20 -0
  5. package/.claude/agents/qe-flaky-test-hunter.md +20 -0
  6. package/.claude/agents/qe-fleet-commander.md +20 -0
  7. package/.claude/agents/qe-performance-tester.md +21 -0
  8. package/.claude/agents/qe-production-intelligence.md +20 -0
  9. package/.claude/agents/qe-quality-analyzer.md +20 -0
  10. package/.claude/agents/qe-quality-gate.md +20 -0
  11. package/.claude/agents/qe-regression-risk-analyzer.md +20 -0
  12. package/.claude/agents/qe-requirements-validator.md +20 -0
  13. package/.claude/agents/qe-security-scanner.md +21 -0
  14. package/.claude/agents/qe-test-data-architect.md +19 -0
  15. package/.claude/agents/qe-test-executor.md +20 -0
  16. package/.claude/agents/qe-test-generator.md +22 -0
  17. package/.claude/agents/qe-visual-tester.md +22 -0
  18. package/CHANGELOG.md +252 -0
  19. package/README.md +325 -1019
  20. package/README.md.backup-20251026 +1366 -0
  21. package/bin/aqe-mcp +1 -1
  22. package/dist/agents/BaseAgent.js +1 -1
  23. package/dist/agents/BaseAgent.js.map +1 -1
  24. package/dist/agents/CoverageAnalyzerAgent.js +16 -16
  25. package/dist/agents/CoverageAnalyzerAgent.js.map +1 -1
  26. package/dist/agents/FlakyTestHunterAgent.js +1 -1
  27. package/dist/agents/FlakyTestHunterAgent.js.map +1 -1
  28. package/dist/cli/commands/init.d.ts.map +1 -1
  29. package/dist/cli/commands/init.js +71 -37
  30. package/dist/cli/commands/init.js.map +1 -1
  31. package/dist/cli/commands/routing/index.d.ts +1 -1
  32. package/dist/cli/commands/routing/index.d.ts.map +1 -1
  33. package/dist/cli/commands/routing/index.js +29 -19
  34. package/dist/cli/commands/routing/index.js.map +1 -1
  35. package/dist/core/memory/EnhancedAgentDBService.d.ts +127 -0
  36. package/dist/core/memory/EnhancedAgentDBService.d.ts.map +1 -0
  37. package/dist/core/memory/EnhancedAgentDBService.js +298 -0
  38. package/dist/core/memory/EnhancedAgentDBService.js.map +1 -0
  39. package/dist/core/neural/NeuralTrainer.js +2 -2
  40. package/dist/core/neural/NeuralTrainer.js.map +1 -1
  41. package/dist/learning/ExperienceReplayBuffer.d.ts +143 -0
  42. package/dist/learning/ExperienceReplayBuffer.d.ts.map +1 -0
  43. package/dist/learning/ExperienceReplayBuffer.js +255 -0
  44. package/dist/learning/ExperienceReplayBuffer.js.map +1 -0
  45. package/dist/learning/FixRecommendationEngine.d.ts +68 -0
  46. package/dist/learning/FixRecommendationEngine.d.ts.map +1 -0
  47. package/dist/learning/FixRecommendationEngine.js +500 -0
  48. package/dist/learning/FixRecommendationEngine.js.map +1 -0
  49. package/dist/learning/FlakyTestDetector.d.ts +19 -0
  50. package/dist/learning/FlakyTestDetector.d.ts.map +1 -1
  51. package/dist/learning/FlakyTestDetector.js +121 -2
  52. package/dist/learning/FlakyTestDetector.js.map +1 -1
  53. package/dist/learning/LearningEngine.d.ts +50 -1
  54. package/dist/learning/LearningEngine.d.ts.map +1 -1
  55. package/dist/learning/LearningEngine.js +140 -0
  56. package/dist/learning/LearningEngine.js.map +1 -1
  57. package/dist/learning/QLearning.d.ts +154 -0
  58. package/dist/learning/QLearning.d.ts.map +1 -0
  59. package/dist/learning/QLearning.js +337 -0
  60. package/dist/learning/QLearning.js.map +1 -0
  61. package/dist/learning/index.d.ts +4 -0
  62. package/dist/learning/index.d.ts.map +1 -1
  63. package/dist/learning/index.js +7 -0
  64. package/dist/learning/index.js.map +1 -1
  65. package/dist/learning/types.d.ts +11 -0
  66. package/dist/learning/types.d.ts.map +1 -1
  67. package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.d.ts +11 -1
  68. package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.d.ts.map +1 -1
  69. package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.js +12 -0
  70. package/dist/mcp/streaming/CoverageAnalyzeStreamHandler.js.map +1 -1
  71. package/dist/mcp/streaming/TestExecuteStreamHandler.d.ts +10 -1
  72. package/dist/mcp/streaming/TestExecuteStreamHandler.d.ts.map +1 -1
  73. package/dist/mcp/streaming/TestExecuteStreamHandler.js +11 -0
  74. package/dist/mcp/streaming/TestExecuteStreamHandler.js.map +1 -1
  75. package/dist/reasoning/PatternQualityScorer.d.ts +134 -0
  76. package/dist/reasoning/PatternQualityScorer.d.ts.map +1 -0
  77. package/dist/reasoning/PatternQualityScorer.js +340 -0
  78. package/dist/reasoning/PatternQualityScorer.js.map +1 -0
  79. package/dist/reasoning/QEReasoningBank.d.ts +138 -4
  80. package/dist/reasoning/QEReasoningBank.d.ts.map +1 -1
  81. package/dist/reasoning/QEReasoningBank.js +560 -12
  82. package/dist/reasoning/QEReasoningBank.js.map +1 -1
  83. package/dist/reasoning/VectorSimilarity.d.ts +131 -0
  84. package/dist/reasoning/VectorSimilarity.d.ts.map +1 -0
  85. package/dist/reasoning/VectorSimilarity.js +250 -0
  86. package/dist/reasoning/VectorSimilarity.js.map +1 -0
  87. package/dist/reasoning/index.d.ts +8 -1
  88. package/dist/reasoning/index.d.ts.map +1 -1
  89. package/dist/reasoning/index.js +13 -2
  90. package/dist/reasoning/index.js.map +1 -1
  91. package/dist/streaming/BaseStreamHandler.d.ts +89 -0
  92. package/dist/streaming/BaseStreamHandler.d.ts.map +1 -0
  93. package/dist/streaming/BaseStreamHandler.js +168 -0
  94. package/dist/streaming/BaseStreamHandler.js.map +1 -0
  95. package/dist/streaming/TestGenerateStreamHandler.d.ts +103 -0
  96. package/dist/streaming/TestGenerateStreamHandler.d.ts.map +1 -0
  97. package/dist/streaming/TestGenerateStreamHandler.js +321 -0
  98. package/dist/streaming/TestGenerateStreamHandler.js.map +1 -0
  99. package/dist/streaming/index.d.ts +16 -0
  100. package/dist/streaming/index.d.ts.map +1 -0
  101. package/dist/streaming/index.js +39 -0
  102. package/dist/streaming/index.js.map +1 -0
  103. package/dist/utils/__mocks__/Logger.d.ts +26 -0
  104. package/dist/utils/__mocks__/Logger.d.ts.map +1 -0
  105. package/dist/utils/__mocks__/Logger.js +42 -0
  106. package/dist/utils/__mocks__/Logger.js.map +1 -0
  107. package/package.json +15 -3
  108. package/dist/agents/mixins/NeuralCapableMixin.d.ts +0 -130
  109. package/dist/agents/mixins/NeuralCapableMixin.d.ts.map +0 -1
  110. package/dist/agents/mixins/NeuralCapableMixin.js +0 -358
  111. package/dist/agents/mixins/NeuralCapableMixin.js.map +0 -1
  112. package/dist/agents/mixins/QUICCapableMixin.d.ts +0 -34
  113. package/dist/agents/mixins/QUICCapableMixin.d.ts.map +0 -1
  114. package/dist/agents/mixins/QUICCapableMixin.js +0 -346
  115. package/dist/agents/mixins/QUICCapableMixin.js.map +0 -1
  116. package/dist/core/security/CertificateValidator.d.ts +0 -130
  117. package/dist/core/security/CertificateValidator.d.ts.map +0 -1
  118. package/dist/core/security/CertificateValidator.js +0 -376
  119. package/dist/core/security/CertificateValidator.js.map +0 -1
  120. package/dist/core/transport/QUICTransport.d.ts +0 -62
  121. package/dist/core/transport/QUICTransport.d.ts.map +0 -1
  122. package/dist/core/transport/QUICTransport.js +0 -381
  123. package/dist/core/transport/QUICTransport.js.map +0 -1
  124. package/dist/core/transport/SecureQUICTransport.d.ts +0 -71
  125. package/dist/core/transport/SecureQUICTransport.d.ts.map +0 -1
  126. package/dist/core/transport/SecureQUICTransport.js +0 -253
  127. package/dist/core/transport/SecureQUICTransport.js.map +0 -1
  128. package/dist/learning/AdvancedFeatureExtractor.d.ts +0 -123
  129. package/dist/learning/AdvancedFeatureExtractor.d.ts.map +0 -1
  130. package/dist/learning/AdvancedFeatureExtractor.js +0 -423
  131. package/dist/learning/AdvancedFeatureExtractor.js.map +0 -1
  132. package/dist/learning/NeuralPatternMatcher.d.ts +0 -184
  133. package/dist/learning/NeuralPatternMatcher.d.ts.map +0 -1
  134. package/dist/learning/NeuralPatternMatcher.js +0 -702
  135. package/dist/learning/NeuralPatternMatcher.js.map +0 -1
  136. package/dist/learning/NeuralTrainer.d.ts +0 -209
  137. package/dist/learning/NeuralTrainer.d.ts.map +0 -1
  138. package/dist/learning/NeuralTrainer.js +0 -478
  139. package/dist/learning/NeuralTrainer.js.map +0 -1
  140. package/dist/transport/QUICTransport.d.ts +0 -340
  141. package/dist/transport/QUICTransport.d.ts.map +0 -1
  142. package/dist/transport/QUICTransport.js +0 -814
  143. package/dist/transport/QUICTransport.js.map +0 -1
  144. package/dist/transport/UDPTransport.d.ts +0 -348
  145. package/dist/transport/UDPTransport.d.ts.map +0 -1
  146. package/dist/transport/UDPTransport.js +0 -820
  147. package/dist/transport/UDPTransport.js.map +0 -1
@@ -29,8 +29,43 @@
29
29
  * });
30
30
  * ```
31
31
  */
32
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
33
+ if (k2 === undefined) k2 = k;
34
+ var desc = Object.getOwnPropertyDescriptor(m, k);
35
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
36
+ desc = { enumerable: true, get: function() { return m[k]; } };
37
+ }
38
+ Object.defineProperty(o, k2, desc);
39
+ }) : (function(o, m, k, k2) {
40
+ if (k2 === undefined) k2 = k;
41
+ o[k2] = m[k];
42
+ }));
43
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
44
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
45
+ }) : function(o, v) {
46
+ o["default"] = v;
47
+ });
48
+ var __importStar = (this && this.__importStar) || (function () {
49
+ var ownKeys = function(o) {
50
+ ownKeys = Object.getOwnPropertyNames || function (o) {
51
+ var ar = [];
52
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
53
+ return ar;
54
+ };
55
+ return ownKeys(o);
56
+ };
57
+ return function (mod) {
58
+ if (mod && mod.__esModule) return mod;
59
+ var result = {};
60
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
61
+ __setModuleDefault(result, mod);
62
+ return result;
63
+ };
64
+ })();
32
65
  Object.defineProperty(exports, "__esModule", { value: true });
33
66
  exports.QEReasoningBank = void 0;
67
+ const VectorSimilarity_1 = require("./VectorSimilarity");
68
+ const PatternQualityScorer_1 = require("./PatternQualityScorer");
34
69
  /**
35
70
  * QEReasoningBank - Main class for pattern storage and retrieval
36
71
  *
@@ -49,13 +84,27 @@ exports.QEReasoningBank = void 0;
49
84
  * @public
50
85
  */
51
86
  class QEReasoningBank {
52
- constructor() {
87
+ constructor(config = {}) {
53
88
  this.patterns = new Map();
54
89
  this.patternIndex = new Map();
90
+ this.keywordIndex = new Map(); // NEW: keyword-based fast lookup
91
+ this.frameworkIndex = new Map(); // NEW: framework-based index
55
92
  this.versionHistory = new Map();
93
+ this.vectorCache = new Map();
94
+ this.similarityCache = new Map(); // NEW: cache for similar patterns
95
+ this.cacheExpiryTime = 5 * 60 * 1000; // 5 minutes cache TTL
96
+ this.lastCacheCleanup = Date.now();
97
+ this.vectorSimilarity = new VectorSimilarity_1.VectorSimilarity({ useIDF: true });
98
+ this.qualityScorer = new PatternQualityScorer_1.PatternQualityScorer();
99
+ this.minQuality = config.minQuality ?? 0.7;
100
+ this.performanceMetrics = {
101
+ lookupTimes: [],
102
+ cachehits: 0,
103
+ cacheMisses: 0
104
+ };
56
105
  }
57
106
  /**
58
- * Store a new test pattern
107
+ * Store a new test pattern with quality scoring and vector indexing
59
108
  */
60
109
  async storePattern(pattern) {
61
110
  // Validate pattern
@@ -65,6 +114,22 @@ class QEReasoningBank {
65
114
  if (pattern.confidence < 0 || pattern.confidence > 1) {
66
115
  throw new Error('Confidence must be between 0 and 1');
67
116
  }
117
+ // Calculate pattern quality if not provided
118
+ if (pattern.quality === undefined) {
119
+ const qualityScore = this.qualityScorer.calculateQuality({
120
+ id: pattern.id,
121
+ name: pattern.name,
122
+ code: pattern.examples[0] || pattern.template,
123
+ template: pattern.template,
124
+ description: pattern.description,
125
+ tags: pattern.metadata.tags,
126
+ usageCount: pattern.usageCount,
127
+ metadata: {
128
+ successRate: pattern.successRate
129
+ }
130
+ });
131
+ pattern.quality = qualityScore.overall;
132
+ }
68
133
  // Version existing pattern
69
134
  if (this.patterns.has(pattern.id)) {
70
135
  const existing = this.patterns.get(pattern.id);
@@ -74,6 +139,11 @@ class QEReasoningBank {
74
139
  }
75
140
  // Store pattern
76
141
  this.patterns.set(pattern.id, { ...pattern });
142
+ // Generate and cache vector embedding
143
+ const patternText = this.getPatternText(pattern);
144
+ this.vectorSimilarity.indexDocument(patternText);
145
+ const vector = this.vectorSimilarity.generateEmbedding(patternText);
146
+ this.vectorCache.set(pattern.id, vector);
77
147
  // Update index for fast lookup
78
148
  this.updateIndex(pattern);
79
149
  }
@@ -84,24 +154,163 @@ class QEReasoningBank {
84
154
  return this.patterns.get(id) || null;
85
155
  }
86
156
  /**
87
- * Find matching patterns for a code context
157
+ * Find matching patterns using vector similarity with performance optimizations
158
+ * Target: 85%+ matching accuracy, <50ms p95 latency
159
+ *
160
+ * **Optimizations:**
161
+ * - Caching: Frequently accessed patterns cached for 5 minutes
162
+ * - Indexing: Multi-level indexes (keyword, framework, tag) for fast lookup
163
+ * - Early termination: Stop processing when enough high-quality matches found
164
+ * - Batch processing: Process multiple candidates in batches
88
165
  */
89
166
  async findMatchingPatterns(context, limit = 10) {
167
+ const startTime = performance.now();
168
+ // Check cache first
169
+ const cacheKey = this.getCacheKey(context);
170
+ if (this.similarityCache.has(cacheKey)) {
171
+ this.performanceMetrics.cachehits++;
172
+ const cached = this.similarityCache.get(cacheKey);
173
+ this.recordLookupTime(performance.now() - startTime);
174
+ return cached.slice(0, limit);
175
+ }
176
+ this.performanceMetrics.cacheMisses++;
177
+ // Use indexed lookup for fast candidate selection
178
+ const candidates = this.getIndexedCandidates(context);
179
+ // If we have indexed candidates, use them; otherwise fall back to full scan
180
+ const vectorMatches = candidates.length > 0
181
+ ? this.scoreIndexedCandidates(candidates, context, limit * 2)
182
+ : await this.vectorSimilaritySearch(context, limit * 2);
90
183
  const matches = [];
91
- for (const pattern of Array.from(this.patterns.values())) {
92
- const confidence = this.calculateMatchConfidence(pattern, context);
93
- if (confidence > 0.3) { // Threshold
184
+ let highQualityCount = 0;
185
+ // Process candidates with early termination
186
+ for (const vectorMatch of vectorMatches) {
187
+ const pattern = this.patterns.get(vectorMatch.id);
188
+ if (!pattern)
189
+ continue;
190
+ // Calculate hybrid confidence (vector similarity + rule-based)
191
+ const ruleBased = this.calculateMatchConfidence(pattern, context);
192
+ const vectorSim = vectorMatch.similarity;
193
+ // Hybrid scoring: 60% vector similarity, 40% rule-based
194
+ const confidence = vectorSim * 0.6 + ruleBased * 0.4;
195
+ // Apply quality filter
196
+ const meetsQuality = (pattern.quality ?? 1.0) >= this.minQuality;
197
+ if (confidence > 0.3 && meetsQuality) {
198
+ const applicability = confidence * pattern.successRate * (pattern.quality ?? 1.0);
94
199
  matches.push({
95
200
  pattern,
96
201
  confidence,
97
- reasoning: this.generateReasoning(pattern, context),
98
- applicability: confidence * pattern.successRate
202
+ similarity: vectorSim,
203
+ reasoning: this.generateReasoning(pattern, context, vectorSim),
204
+ applicability
99
205
  });
206
+ // Early termination: if we have enough high-quality matches, stop
207
+ if (applicability > 0.85) {
208
+ highQualityCount++;
209
+ if (highQualityCount >= limit && matches.length >= limit * 1.5) {
210
+ break;
211
+ }
212
+ }
100
213
  }
101
214
  }
102
- // Sort by applicability
215
+ // Sort by applicability (confidence × success rate × quality)
103
216
  matches.sort((a, b) => b.applicability - a.applicability);
104
- return matches.slice(0, limit);
217
+ const result = matches.slice(0, limit);
218
+ // Cache the result
219
+ this.similarityCache.set(cacheKey, result);
220
+ this.cleanupCacheIfNeeded();
221
+ const lookupTime = performance.now() - startTime;
222
+ this.recordLookupTime(lookupTime);
223
+ return result;
224
+ }
225
+ /**
226
+ * Get cache key for context
227
+ */
228
+ getCacheKey(context) {
229
+ return JSON.stringify({
230
+ codeType: context.codeType,
231
+ framework: context.framework,
232
+ language: context.language,
233
+ keywords: context.keywords?.sort()
234
+ });
235
+ }
236
+ /**
237
+ * Get indexed candidates for fast lookup
238
+ * Uses multi-level indexes (framework, keywords, tags)
239
+ */
240
+ getIndexedCandidates(context) {
241
+ const candidateIds = new Set();
242
+ // Framework index lookup (most specific)
243
+ if (context.framework && this.frameworkIndex.has(context.framework)) {
244
+ this.frameworkIndex.get(context.framework).forEach(id => candidateIds.add(id));
245
+ }
246
+ // Keyword index lookup
247
+ if (context.keywords && context.keywords.length > 0) {
248
+ for (const keyword of context.keywords) {
249
+ if (this.keywordIndex.has(keyword.toLowerCase())) {
250
+ this.keywordIndex.get(keyword.toLowerCase()).forEach(id => candidateIds.add(id));
251
+ }
252
+ }
253
+ }
254
+ return Array.from(candidateIds);
255
+ }
256
+ /**
257
+ * Score indexed candidates efficiently
258
+ */
259
+ scoreIndexedCandidates(candidateIds, context, limit) {
260
+ const queryText = this.buildQueryText(context);
261
+ const queryVector = this.vectorSimilarity.generateEmbedding(queryText);
262
+ const scored = candidateIds
263
+ .map(id => {
264
+ const vector = this.vectorCache.get(id);
265
+ if (!vector)
266
+ return null;
267
+ const similarity = this.vectorSimilarity.cosineSimilarity(queryVector, vector);
268
+ return { id, similarity };
269
+ })
270
+ .filter((item) => item !== null)
271
+ .sort((a, b) => b.similarity - a.similarity)
272
+ .slice(0, limit);
273
+ return scored;
274
+ }
275
+ /**
276
+ * Fallback vector similarity search
277
+ */
278
+ async vectorSimilaritySearch(context, limit) {
279
+ const queryText = this.buildQueryText(context);
280
+ const queryVector = this.vectorSimilarity.generateEmbedding(queryText);
281
+ return this.vectorSimilarity.findTopK(queryVector, this.vectorCache, limit);
282
+ }
283
+ /**
284
+ * Record lookup time for performance monitoring
285
+ */
286
+ recordLookupTime(timeMs) {
287
+ this.performanceMetrics.lookupTimes.push(timeMs);
288
+ // Keep only last 1000 measurements
289
+ if (this.performanceMetrics.lookupTimes.length > 1000) {
290
+ this.performanceMetrics.lookupTimes.shift();
291
+ }
292
+ }
293
+ /**
294
+ * Cleanup cache if needed (every 5 minutes)
295
+ */
296
+ cleanupCacheIfNeeded() {
297
+ const now = Date.now();
298
+ if (now - this.lastCacheCleanup > this.cacheExpiryTime) {
299
+ this.similarityCache.clear();
300
+ this.lastCacheCleanup = now;
301
+ }
302
+ }
303
+ /**
304
+ * Find similar patterns by example code
305
+ * Uses vector similarity for code-based matching
306
+ */
307
+ async findSimilarPatterns(exampleCode, framework, limit = 5) {
308
+ return this.findMatchingPatterns({
309
+ codeType: 'test',
310
+ framework,
311
+ sourceCode: exampleCode,
312
+ keywords: this.extractKeywords(exampleCode)
313
+ }, limit);
105
314
  }
106
315
  /**
107
316
  * Update pattern success metrics
@@ -121,7 +330,7 @@ class QEReasoningBank {
121
330
  pattern.metadata.updatedAt = new Date();
122
331
  }
123
332
  /**
124
- * Get pattern statistics
333
+ * Get pattern statistics (alias for getStats for backward compatibility)
125
334
  */
126
335
  async getStatistics() {
127
336
  const patterns = Array.from(this.patterns.values());
@@ -129,6 +338,7 @@ class QEReasoningBank {
129
338
  totalPatterns: patterns.length,
130
339
  averageConfidence: patterns.reduce((sum, p) => sum + p.confidence, 0) / patterns.length || 0,
131
340
  averageSuccessRate: patterns.reduce((sum, p) => sum + p.successRate, 0) / patterns.length || 0,
341
+ averageQuality: patterns.reduce((sum, p) => sum + (p.quality ?? 0), 0) / patterns.length || 0,
132
342
  byCategory: {},
133
343
  byFramework: {}
134
344
  };
@@ -173,12 +383,33 @@ class QEReasoningBank {
173
383
  this.patternIndex.set(pattern.category, new Set());
174
384
  }
175
385
  this.patternIndex.get(pattern.category).add(pattern.id);
386
+ // Index by framework (NEW: fast framework lookup)
387
+ if (!this.frameworkIndex.has(pattern.framework)) {
388
+ this.frameworkIndex.set(pattern.framework, new Set());
389
+ }
390
+ this.frameworkIndex.get(pattern.framework).add(pattern.id);
176
391
  // Index by tags
177
392
  for (const tag of pattern.metadata.tags) {
178
393
  if (!this.patternIndex.has(`tag:${tag}`)) {
179
394
  this.patternIndex.set(`tag:${tag}`, new Set());
180
395
  }
181
396
  this.patternIndex.get(`tag:${tag}`).add(pattern.id);
397
+ // Index by keyword (NEW: fast keyword lookup)
398
+ const keyword = tag.toLowerCase();
399
+ if (!this.keywordIndex.has(keyword)) {
400
+ this.keywordIndex.set(keyword, new Set());
401
+ }
402
+ this.keywordIndex.get(keyword).add(pattern.id);
403
+ }
404
+ // Index name keywords (NEW)
405
+ const nameWords = pattern.name.toLowerCase().split(/\s+/);
406
+ for (const word of nameWords) {
407
+ if (word.length > 2) { // Skip very short words
408
+ if (!this.keywordIndex.has(word)) {
409
+ this.keywordIndex.set(word, new Set());
410
+ }
411
+ this.keywordIndex.get(word).add(pattern.id);
412
+ }
182
413
  }
183
414
  }
184
415
  calculateMatchConfidence(pattern, context) {
@@ -211,8 +442,12 @@ class QEReasoningBank {
211
442
  score += pattern.confidence * 0.10;
212
443
  return factors > 0 ? Math.min(score, 1.0) : 0;
213
444
  }
214
- generateReasoning(pattern, context) {
445
+ generateReasoning(pattern, context, similarity) {
215
446
  const reasons = [];
447
+ // Vector similarity score
448
+ if (similarity !== undefined) {
449
+ reasons.push(`Similarity: ${(similarity * 100).toFixed(1)}%`);
450
+ }
216
451
  if (context.framework && pattern.framework === context.framework) {
217
452
  reasons.push(`Framework match: ${pattern.framework}`);
218
453
  }
@@ -225,10 +460,323 @@ class QEReasoningBank {
225
460
  reasons.push(`Tag matches: ${matchingKeywords.join(', ')}`);
226
461
  }
227
462
  }
463
+ if (pattern.quality !== undefined) {
464
+ reasons.push(`Quality: ${(pattern.quality * 100).toFixed(1)}%`);
465
+ }
228
466
  reasons.push(`Success rate: ${(pattern.successRate * 100).toFixed(1)}%`);
229
467
  reasons.push(`Used ${pattern.usageCount} times`);
230
468
  return reasons.join('; ');
231
469
  }
470
+ /**
471
+ * Build query text from context for vector matching
472
+ */
473
+ buildQueryText(context) {
474
+ const parts = [];
475
+ parts.push(context.codeType);
476
+ if (context.framework) {
477
+ parts.push(context.framework);
478
+ }
479
+ if (context.language) {
480
+ parts.push(context.language);
481
+ }
482
+ if (context.keywords) {
483
+ parts.push(...context.keywords);
484
+ }
485
+ if (context.sourceCode) {
486
+ // Extract meaningful tokens from source code
487
+ parts.push(...this.extractKeywords(context.sourceCode));
488
+ }
489
+ return parts.join(' ');
490
+ }
491
+ /**
492
+ * Get pattern text for vector embedding
493
+ */
494
+ getPatternText(pattern) {
495
+ const parts = [];
496
+ parts.push(pattern.name);
497
+ parts.push(pattern.description);
498
+ parts.push(pattern.category);
499
+ parts.push(pattern.framework);
500
+ parts.push(...pattern.metadata.tags);
501
+ if (pattern.examples.length > 0) {
502
+ parts.push(...this.extractKeywords(pattern.examples[0]));
503
+ }
504
+ return parts.join(' ');
505
+ }
506
+ /**
507
+ * Extract keywords from code
508
+ */
509
+ extractKeywords(code) {
510
+ // Simple keyword extraction (can be enhanced with AST analysis)
511
+ const keywords = [];
512
+ // Extract function/method names
513
+ const functionMatches = code.match(/function\s+(\w+)|const\s+(\w+)\s*=/g);
514
+ if (functionMatches) {
515
+ keywords.push(...functionMatches.map(m => m.replace(/function|const|=|\s/g, '')));
516
+ }
517
+ // Extract identifiers (camelCase, snake_case)
518
+ const identifiers = code.match(/\b[a-z_][a-zA-Z0-9_]{2,}\b/g);
519
+ if (identifiers) {
520
+ keywords.push(...identifiers.slice(0, 20)); // Limit to avoid noise
521
+ }
522
+ return keywords.filter(k => k.length > 2);
523
+ }
524
+ /**
525
+ * Match a pattern by exact ID
526
+ * Alias for getPattern for consistency with pattern matching API
527
+ */
528
+ async matchPattern(patternId) {
529
+ return this.getPattern(patternId);
530
+ }
531
+ /**
532
+ * Find patterns using flexible search criteria
533
+ * Supports multiple search strategies: vector similarity, tag matching, keyword search
534
+ */
535
+ async findPattern(criteria, limit = 10) {
536
+ // If query provided, use vector similarity search
537
+ if (criteria.query) {
538
+ return this.findMatchingPatterns({
539
+ codeType: 'test',
540
+ framework: criteria.framework,
541
+ language: criteria.language,
542
+ keywords: criteria.tags,
543
+ sourceCode: criteria.query
544
+ }, limit);
545
+ }
546
+ // Otherwise, use filter-based search
547
+ let patterns = Array.from(this.patterns.values());
548
+ // Apply filters
549
+ if (criteria.category) {
550
+ patterns = patterns.filter(p => p.category === criteria.category);
551
+ }
552
+ if (criteria.framework) {
553
+ patterns = patterns.filter(p => p.framework === criteria.framework);
554
+ }
555
+ if (criteria.language) {
556
+ patterns = patterns.filter(p => p.language === criteria.language);
557
+ }
558
+ if (criteria.tags && criteria.tags.length > 0) {
559
+ patterns = patterns.filter(p => criteria.tags.some(tag => p.metadata.tags.includes(tag)));
560
+ }
561
+ if (criteria.minConfidence !== undefined) {
562
+ patterns = patterns.filter(p => p.confidence >= criteria.minConfidence);
563
+ }
564
+ if (criteria.minQuality !== undefined) {
565
+ patterns = patterns.filter(p => (p.quality ?? 0) >= criteria.minQuality);
566
+ }
567
+ // Sort by quality and success rate
568
+ patterns.sort((a, b) => {
569
+ const scoreA = (a.quality ?? 0.5) * a.successRate * a.confidence;
570
+ const scoreB = (b.quality ?? 0.5) * b.successRate * b.confidence;
571
+ return scoreB - scoreA;
572
+ });
573
+ // Convert to PatternMatch format
574
+ return patterns.slice(0, limit).map(pattern => ({
575
+ pattern,
576
+ confidence: pattern.confidence,
577
+ similarity: 1.0, // Filter-based match is exact
578
+ reasoning: this.generateReasoning(pattern, {
579
+ codeType: 'test',
580
+ framework: criteria.framework,
581
+ language: criteria.language,
582
+ keywords: criteria.tags
583
+ }),
584
+ applicability: pattern.confidence * pattern.successRate * (pattern.quality ?? 1.0)
585
+ }));
586
+ }
587
+ /**
588
+ * Calculate similarity score between two patterns
589
+ * Returns a score between 0-1 indicating how similar the patterns are
590
+ */
591
+ calculateSimilarity(pattern1, pattern2) {
592
+ let score = 0;
593
+ let weights = 0;
594
+ // Framework match (25% weight)
595
+ weights += 0.25;
596
+ if (pattern1.framework === pattern2.framework) {
597
+ score += 0.25;
598
+ }
599
+ // Language match (20% weight)
600
+ weights += 0.20;
601
+ if (pattern1.language === pattern2.language) {
602
+ score += 0.20;
603
+ }
604
+ // Category match (20% weight)
605
+ weights += 0.20;
606
+ if (pattern1.category === pattern2.category) {
607
+ score += 0.20;
608
+ }
609
+ // Tag overlap (35% weight)
610
+ weights += 0.35;
611
+ const tags1 = new Set(pattern1.metadata.tags);
612
+ const tags2 = new Set(pattern2.metadata.tags);
613
+ const intersection = new Set([...tags1].filter(t => tags2.has(t)));
614
+ const union = new Set([...tags1, ...tags2]);
615
+ const jaccardSimilarity = union.size > 0 ? intersection.size / union.size : 0;
616
+ score += jaccardSimilarity * 0.35;
617
+ return score / weights;
618
+ }
619
+ /**
620
+ * Search patterns by similarity to a reference pattern
621
+ */
622
+ async searchSimilarPatterns(referencePattern, limit = 5) {
623
+ const matches = [];
624
+ for (const pattern of Array.from(this.patterns.values())) {
625
+ if (pattern.id === referencePattern.id)
626
+ continue;
627
+ const similarity = this.calculateSimilarity(referencePattern, pattern);
628
+ if (similarity > 0.3) {
629
+ matches.push({
630
+ pattern,
631
+ confidence: pattern.confidence,
632
+ similarity,
633
+ reasoning: `Similar pattern: ${(similarity * 100).toFixed(1)}% match`,
634
+ applicability: similarity * pattern.successRate * (pattern.quality ?? 1.0)
635
+ });
636
+ }
637
+ }
638
+ // Sort by similarity and quality
639
+ matches.sort((a, b) => b.applicability - a.applicability);
640
+ return matches.slice(0, limit);
641
+ }
642
+ /**
643
+ * Load patterns from registry file
644
+ */
645
+ async loadFromRegistry(registryPath) {
646
+ try {
647
+ const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
648
+ const registryData = await fs.readFile(registryPath, 'utf-8');
649
+ const registry = JSON.parse(registryData);
650
+ if (!registry.patterns || !Array.isArray(registry.patterns)) {
651
+ throw new Error('Invalid registry format: patterns array not found');
652
+ }
653
+ let loadedCount = 0;
654
+ for (const pattern of registry.patterns) {
655
+ // Convert date strings to Date objects
656
+ if (pattern.metadata?.createdAt) {
657
+ pattern.metadata.createdAt = new Date(pattern.metadata.createdAt);
658
+ }
659
+ if (pattern.metadata?.updatedAt) {
660
+ pattern.metadata.updatedAt = new Date(pattern.metadata.updatedAt);
661
+ }
662
+ await this.storePattern(pattern);
663
+ loadedCount++;
664
+ }
665
+ return loadedCount;
666
+ }
667
+ catch (error) {
668
+ throw new Error(`Failed to load patterns from registry: ${error instanceof Error ? error.message : String(error)}`);
669
+ }
670
+ }
671
+ /**
672
+ * Save patterns to registry file
673
+ */
674
+ async saveToRegistry(registryPath, filter) {
675
+ try {
676
+ const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
677
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
678
+ const patterns = this.exportPatterns(filter);
679
+ const stats = this.getStats();
680
+ const registry = {
681
+ version: '1.0.0',
682
+ lastUpdated: new Date().toISOString(),
683
+ patterns,
684
+ statistics: stats
685
+ };
686
+ // Ensure directory exists
687
+ const dir = path.dirname(registryPath);
688
+ await fs.mkdir(dir, { recursive: true });
689
+ await fs.writeFile(registryPath, JSON.stringify(registry, null, 2), 'utf-8');
690
+ }
691
+ catch (error) {
692
+ throw new Error(`Failed to save patterns to registry: ${error instanceof Error ? error.message : String(error)}`);
693
+ }
694
+ }
695
+ /**
696
+ * Export patterns for cross-project sharing
697
+ */
698
+ exportPatterns(filter) {
699
+ let patterns = Array.from(this.patterns.values());
700
+ if (filter) {
701
+ patterns = patterns.filter(p => {
702
+ if (filter.framework && p.framework !== filter.framework)
703
+ return false;
704
+ if (filter.category && p.category !== filter.category)
705
+ return false;
706
+ if (filter.language && p.language !== filter.language)
707
+ return false;
708
+ return true;
709
+ });
710
+ }
711
+ return patterns;
712
+ }
713
+ /**
714
+ * Import patterns from another project
715
+ */
716
+ async importPatterns(patterns) {
717
+ for (const pattern of patterns) {
718
+ await this.storePattern(pattern);
719
+ }
720
+ }
721
+ /**
722
+ * Get pattern statistics including quality metrics
723
+ */
724
+ getStats() {
725
+ const patterns = Array.from(this.patterns.values());
726
+ const stats = {
727
+ totalPatterns: patterns.length,
728
+ byFramework: {},
729
+ byCategory: {},
730
+ averageQuality: patterns.reduce((sum, p) => sum + (p.quality ?? 0), 0) / patterns.length || 0,
731
+ averageSuccessRate: patterns.reduce((sum, p) => sum + p.successRate, 0) / patterns.length || 0
732
+ };
733
+ for (const pattern of patterns) {
734
+ stats.byFramework[pattern.framework] = (stats.byFramework[pattern.framework] || 0) + 1;
735
+ stats.byCategory[pattern.category] = (stats.byCategory[pattern.category] || 0) + 1;
736
+ }
737
+ return stats;
738
+ }
739
+ /**
740
+ * Get performance metrics (NEW)
741
+ * Returns timing statistics for pattern matching operations
742
+ */
743
+ getPerformanceMetrics() {
744
+ const times = this.performanceMetrics.lookupTimes;
745
+ if (times.length === 0) {
746
+ return {
747
+ avgLookupTime: 0,
748
+ p95LookupTime: 0,
749
+ p99LookupTime: 0,
750
+ cacheHitRate: 0,
751
+ totalLookups: 0
752
+ };
753
+ }
754
+ const sorted = [...times].sort((a, b) => a - b);
755
+ const avg = times.reduce((sum, t) => sum + t, 0) / times.length;
756
+ const p95Index = Math.floor(sorted.length * 0.95);
757
+ const p99Index = Math.floor(sorted.length * 0.99);
758
+ const totalRequests = this.performanceMetrics.cachehits + this.performanceMetrics.cacheMisses;
759
+ const cacheHitRate = totalRequests > 0
760
+ ? (this.performanceMetrics.cachehits / totalRequests) * 100
761
+ : 0;
762
+ return {
763
+ avgLookupTime: parseFloat(avg.toFixed(2)),
764
+ p95LookupTime: parseFloat(sorted[p95Index].toFixed(2)),
765
+ p99LookupTime: parseFloat(sorted[p99Index].toFixed(2)),
766
+ cacheHitRate: parseFloat(cacheHitRate.toFixed(2)),
767
+ totalLookups: times.length
768
+ };
769
+ }
770
+ /**
771
+ * Reset performance metrics (for testing)
772
+ */
773
+ resetPerformanceMetrics() {
774
+ this.performanceMetrics = {
775
+ lookupTimes: [],
776
+ cachehits: 0,
777
+ cacheMisses: 0
778
+ };
779
+ }
232
780
  }
233
781
  exports.QEReasoningBank = QEReasoningBank;
234
782
  exports.default = QEReasoningBank;