aios-core 4.2.13 → 4.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aios-core/core/code-intel/helpers/dev-helper.js +206 -0
- package/.aios-core/core/registry/registry-schema.json +166 -166
- package/.aios-core/core/synapse/diagnostics/collectors/hook-collector.js +3 -3
- package/.aios-core/data/entity-registry.yaml +27 -0
- package/.aios-core/development/scripts/approval-workflow.js +642 -642
- package/.aios-core/development/scripts/backup-manager.js +606 -606
- package/.aios-core/development/scripts/branch-manager.js +389 -389
- package/.aios-core/development/scripts/code-quality-improver.js +1311 -1311
- package/.aios-core/development/scripts/commit-message-generator.js +849 -849
- package/.aios-core/development/scripts/conflict-resolver.js +674 -674
- package/.aios-core/development/scripts/dependency-analyzer.js +637 -637
- package/.aios-core/development/scripts/diff-generator.js +351 -351
- package/.aios-core/development/scripts/elicitation-engine.js +384 -384
- package/.aios-core/development/scripts/elicitation-session-manager.js +299 -299
- package/.aios-core/development/scripts/git-wrapper.js +461 -461
- package/.aios-core/development/scripts/manifest-preview.js +244 -244
- package/.aios-core/development/scripts/metrics-tracker.js +775 -775
- package/.aios-core/development/scripts/modification-validator.js +554 -554
- package/.aios-core/development/scripts/pattern-learner.js +1224 -1224
- package/.aios-core/development/scripts/performance-analyzer.js +757 -757
- package/.aios-core/development/scripts/refactoring-suggester.js +1138 -1138
- package/.aios-core/development/scripts/rollback-handler.js +530 -530
- package/.aios-core/development/scripts/security-checker.js +358 -358
- package/.aios-core/development/scripts/template-engine.js +239 -239
- package/.aios-core/development/scripts/template-validator.js +278 -278
- package/.aios-core/development/scripts/test-generator.js +843 -843
- package/.aios-core/development/scripts/transaction-manager.js +589 -589
- package/.aios-core/development/scripts/usage-tracker.js +673 -673
- package/.aios-core/development/scripts/validate-filenames.js +226 -226
- package/.aios-core/development/scripts/version-tracker.js +526 -526
- package/.aios-core/development/scripts/yaml-validator.js +396 -396
- package/.aios-core/development/tasks/build-autonomous.md +10 -4
- package/.aios-core/development/tasks/create-service.md +23 -0
- package/.aios-core/development/tasks/dev-develop-story.md +12 -6
- package/.aios-core/development/tasks/dev-suggest-refactoring.md +7 -1
- package/.aios-core/development/tasks/publish-npm.md +3 -3
- package/.aios-core/hooks/unified/README.md +1 -1
- package/.aios-core/install-manifest.yaml +65 -61
- package/.aios-core/manifests/schema/manifest-schema.json +190 -190
- package/.aios-core/product/templates/component-react-tmpl.tsx +98 -98
- package/.aios-core/product/templates/engine/schemas/adr.schema.json +102 -102
- package/.aios-core/product/templates/engine/schemas/dbdr.schema.json +205 -205
- package/.aios-core/product/templates/engine/schemas/epic.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/pmdr.schema.json +175 -175
- package/.aios-core/product/templates/engine/schemas/prd-v2.schema.json +300 -300
- package/.aios-core/product/templates/engine/schemas/prd.schema.json +152 -152
- package/.aios-core/product/templates/engine/schemas/story.schema.json +222 -222
- package/.aios-core/product/templates/engine/schemas/task.schema.json +154 -154
- package/.aios-core/product/templates/eslintrc-security.json +32 -32
- package/.aios-core/product/templates/github-actions-cd.yml +212 -212
- package/.aios-core/product/templates/github-actions-ci.yml +172 -172
- package/.aios-core/product/templates/shock-report-tmpl.html +502 -502
- package/.aios-core/product/templates/token-exports-css-tmpl.css +240 -240
- package/.aios-core/quality/schemas/quality-metrics.schema.json +233 -233
- package/.aios-core/scripts/migrate-framework-docs.sh +300 -300
- package/README.en.md +747 -0
- package/README.md +4 -2
- package/bin/aios.js +7 -4
- package/package.json +1 -1
- package/packages/aios-pro-cli/src/recover.js +1 -1
- package/packages/installer/src/wizard/ide-config-generator.js +6 -6
- package/packages/installer/src/wizard/pro-setup.js +3 -3
- package/pro/license/degradation.js +220 -220
- package/pro/license/errors.js +450 -450
- package/pro/license/feature-gate.js +354 -354
- package/pro/license/index.js +181 -181
- package/pro/license/license-cache.js +523 -523
- package/pro/license/license-crypto.js +303 -303
- package/scripts/package-synapse.js +5 -5
- package/scripts/validate-package-completeness.js +3 -3
- package/.aios-core/.session/current-session.json +0 -14
- package/.aios-core/data/registry-update-log.jsonl +0 -191
- package/.aios-core/docs/SHARD-TRANSLATION-GUIDE.md +0 -335
- package/.aios-core/docs/component-creation-guide.md +0 -458
- package/.aios-core/docs/session-update-pattern.md +0 -307
- package/.aios-core/docs/standards/AIOS-FRAMEWORK-MASTER.md +0 -1963
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1-SUMMARY.md +0 -1190
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO-V2.1.md +0 -439
- package/.aios-core/docs/standards/AIOS-LIVRO-DE-OURO.md +0 -5398
- package/.aios-core/docs/standards/V3-ARCHITECTURAL-DECISIONS.md +0 -523
- package/.aios-core/docs/template-syntax.md +0 -267
- package/.aios-core/docs/troubleshooting-guide.md +0 -625
- package/.aios-core/infrastructure/tests/utilities-audit-results.json +0 -501
- package/.aios-core/manifests/agents.csv +0 -29
- package/.aios-core/manifests/tasks.csv +0 -198
- package/.aios-core/manifests/workers.csv +0 -204
- package/.claude/rules/agent-authority.md +0 -105
- package/.claude/rules/coderabbit-integration.md +0 -93
- package/.claude/rules/ids-principles.md +0 -112
- package/.claude/rules/story-lifecycle.md +0 -139
- package/.claude/rules/workflow-execution.md +0 -150
- package/scripts/glue/README.md +0 -355
- package/scripts/glue/compose-agent-prompt.cjs +0 -362
- /package/.claude/hooks/{precompact-session-digest.js → precompact-session-digest.cjs} +0 -0
- /package/.claude/hooks/{synapse-engine.js → synapse-engine.cjs} +0 -0
|
@@ -1,1225 +1,1225 @@
|
|
|
1
|
-
const fs = require('fs').promises;
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const chalk = require('chalk');
|
|
4
|
-
const EventEmitter = require('events');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Pattern learning system for successful modifications
|
|
8
|
-
* Learns from successful modifications to suggest improvements and automate common patterns
|
|
9
|
-
*/
|
|
10
|
-
class PatternLearner extends EventEmitter {
|
|
11
|
-
constructor(options = {}) {
|
|
12
|
-
super();
|
|
13
|
-
this.rootPath = options.rootPath || process.cwd();
|
|
14
|
-
this.patternsDir = path.join(this.rootPath, '.aios', 'patterns');
|
|
15
|
-
this.historyFile = path.join(this.patternsDir, 'modification_history.json');
|
|
16
|
-
this.patternsFile = path.join(this.patternsDir, 'learned_patterns.json');
|
|
17
|
-
this.patterns = new Map();
|
|
18
|
-
this.modificationHistory = [];
|
|
19
|
-
this.learningThreshold = options.learningThreshold || 3; // Minimum occurrences to learn pattern
|
|
20
|
-
this.similarityThreshold = options.similarityThreshold || 0.8; // 80% similarity
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Initialize pattern learner
|
|
25
|
-
*/
|
|
26
|
-
async initialize() {
|
|
27
|
-
await fs.mkdir(this.patternsDir, { recursive: true });
|
|
28
|
-
await this.loadHistory();
|
|
29
|
-
await this.loadPatterns();
|
|
30
|
-
console.log(chalk.green('✅ Pattern learner initialized'));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Record successful modification for learning
|
|
35
|
-
*/
|
|
36
|
-
async recordSuccessfulModification(modification) {
|
|
37
|
-
const record = {
|
|
38
|
-
id: `mod-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`,
|
|
39
|
-
timestamp: new Date().toISOString(),
|
|
40
|
-
componentType: modification.componentType,
|
|
41
|
-
modificationType: modification.modificationType,
|
|
42
|
-
patterns: await this.extractPatterns(modification),
|
|
43
|
-
outcomes: {
|
|
44
|
-
success: true,
|
|
45
|
-
metrics: modification.metrics || {},
|
|
46
|
-
improvements: modification.improvements || []
|
|
47
|
-
},
|
|
48
|
-
metadata: {
|
|
49
|
-
author: modification.author || process.env.USER || 'unknown',
|
|
50
|
-
duration: modification.duration,
|
|
51
|
-
complexity: modification.complexity
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Add to history
|
|
56
|
-
this.modificationHistory.push(record);
|
|
57
|
-
|
|
58
|
-
// Learn from this modification
|
|
59
|
-
await this.learnFromModification(record);
|
|
60
|
-
|
|
61
|
-
// Save updated history
|
|
62
|
-
await this.saveHistory();
|
|
63
|
-
|
|
64
|
-
// Emit event for real-time learning
|
|
65
|
-
this.emit('modification_recorded', record);
|
|
66
|
-
|
|
67
|
-
console.log(chalk.green(`✅ Recorded successful modification: ${record.id}`));
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
recordId: record.id,
|
|
71
|
-
patternsExtracted: record.patterns.length,
|
|
72
|
-
learningTriggered: await this.checkLearningThreshold(record.patterns)
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Extract patterns from modification
|
|
78
|
-
*/
|
|
79
|
-
async extractPatterns(modification) {
|
|
80
|
-
const patterns = [];
|
|
81
|
-
|
|
82
|
-
// Code change patterns
|
|
83
|
-
if (modification.codeChanges) {
|
|
84
|
-
patterns.push(...this.extractCodePatterns(modification.codeChanges));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Structural patterns
|
|
88
|
-
if (modification.structuralChanges) {
|
|
89
|
-
patterns.push(...this.extractStructuralPatterns(modification.structuralChanges));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Refactoring patterns
|
|
93
|
-
if (modification.refactoringType) {
|
|
94
|
-
patterns.push(...this.extractRefactoringPatterns(modification));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Dependency patterns
|
|
98
|
-
if (modification.dependencyChanges) {
|
|
99
|
-
patterns.push(...this.extractDependencyPatterns(modification.dependencyChanges));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Performance patterns
|
|
103
|
-
if (modification.performanceImprovements) {
|
|
104
|
-
patterns.push(...this.extractPerformancePatterns(modification.performanceImprovements));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return patterns;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Extract code change patterns
|
|
112
|
-
*/
|
|
113
|
-
extractCodePatterns(codeChanges) {
|
|
114
|
-
const patterns = [];
|
|
115
|
-
|
|
116
|
-
for (const change of codeChanges) {
|
|
117
|
-
// Function transformation patterns
|
|
118
|
-
if (change.type === 'function_transformation') {
|
|
119
|
-
patterns.push({
|
|
120
|
-
type: 'code_transformation',
|
|
121
|
-
subtype: 'function',
|
|
122
|
-
from: this.normalizeCode(change.before),
|
|
123
|
-
to: this.normalizeCode(change.after),
|
|
124
|
-
context: change.context,
|
|
125
|
-
benefits: change.benefits || []
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Error handling patterns
|
|
130
|
-
if (change.type === 'error_handling') {
|
|
131
|
-
patterns.push({
|
|
132
|
-
type: 'error_handling',
|
|
133
|
-
subtype: change.handlingType,
|
|
134
|
-
pattern: change.pattern,
|
|
135
|
-
improvement: change.improvement
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Async/await patterns
|
|
140
|
-
if (change.type === 'async_transformation') {
|
|
141
|
-
patterns.push({
|
|
142
|
-
type: 'async_pattern',
|
|
143
|
-
from: change.callbackPattern,
|
|
144
|
-
to: change.asyncPattern,
|
|
145
|
-
complexity_reduction: change.complexityReduction
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// API usage patterns
|
|
150
|
-
if (change.type === 'api_improvement') {
|
|
151
|
-
patterns.push({
|
|
152
|
-
type: 'api_usage',
|
|
153
|
-
oldPattern: change.oldUsage,
|
|
154
|
-
newPattern: change.newUsage,
|
|
155
|
-
benefits: change.benefits
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return patterns;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Extract structural patterns
|
|
165
|
-
*/
|
|
166
|
-
extractStructuralPatterns(structuralChanges) {
|
|
167
|
-
const patterns = [];
|
|
168
|
-
|
|
169
|
-
for (const change of structuralChanges) {
|
|
170
|
-
patterns.push({
|
|
171
|
-
type: 'structural',
|
|
172
|
-
changeType: change.type,
|
|
173
|
-
pattern: {
|
|
174
|
-
before: change.beforeStructure,
|
|
175
|
-
after: change.afterStructure
|
|
176
|
-
},
|
|
177
|
-
benefits: {
|
|
178
|
-
modularity: change.modularityImprovement || 0,
|
|
179
|
-
maintainability: change.maintainabilityImprovement || 0,
|
|
180
|
-
testability: change.testabilityImprovement || 0
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
return patterns;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Extract refactoring patterns
|
|
190
|
-
*/
|
|
191
|
-
extractRefactoringPatterns(modification) {
|
|
192
|
-
const patterns = [];
|
|
193
|
-
|
|
194
|
-
patterns.push({
|
|
195
|
-
type: 'refactoring',
|
|
196
|
-
refactoringType: modification.refactoringType,
|
|
197
|
-
triggers: modification.triggers || [],
|
|
198
|
-
steps: modification.steps || [],
|
|
199
|
-
validation: modification.validation || {},
|
|
200
|
-
benefits: modification.measuredBenefits || {}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return patterns;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Extract dependency patterns
|
|
208
|
-
*/
|
|
209
|
-
extractDependencyPatterns(dependencyChanges) {
|
|
210
|
-
const patterns = [];
|
|
211
|
-
|
|
212
|
-
for (const change of dependencyChanges) {
|
|
213
|
-
if (change.type === 'consolidation') {
|
|
214
|
-
patterns.push({
|
|
215
|
-
type: 'dependency_consolidation',
|
|
216
|
-
from: change.originalDependencies,
|
|
217
|
-
to: change.consolidatedDependency,
|
|
218
|
-
reduction: change.dependencyReduction
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (change.type === 'upgrade') {
|
|
223
|
-
patterns.push({
|
|
224
|
-
type: 'dependency_upgrade',
|
|
225
|
-
dependency: change.dependency,
|
|
226
|
-
fromVersion: change.fromVersion,
|
|
227
|
-
toVersion: change.toVersion,
|
|
228
|
-
migrationSteps: change.migrationSteps
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return patterns;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Extract performance patterns
|
|
238
|
-
*/
|
|
239
|
-
extractPerformancePatterns(performanceImprovements) {
|
|
240
|
-
const patterns = [];
|
|
241
|
-
|
|
242
|
-
for (const improvement of performanceImprovements) {
|
|
243
|
-
patterns.push({
|
|
244
|
-
type: 'performance',
|
|
245
|
-
optimizationType: improvement.type,
|
|
246
|
-
technique: improvement.technique,
|
|
247
|
-
metrics: {
|
|
248
|
-
before: improvement.metricsBefore,
|
|
249
|
-
after: improvement.metricsAfter,
|
|
250
|
-
improvement: improvement.percentageImprovement
|
|
251
|
-
},
|
|
252
|
-
applicableContexts: improvement.contexts || []
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return patterns;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Learn from modification
|
|
261
|
-
*/
|
|
262
|
-
async learnFromModification(record) {
|
|
263
|
-
for (const pattern of record.patterns) {
|
|
264
|
-
const patternKey = this.generatePatternKey(pattern);
|
|
265
|
-
|
|
266
|
-
// Check if similar pattern exists
|
|
267
|
-
const similarPattern = await this.findSimilarPattern(pattern);
|
|
268
|
-
|
|
269
|
-
if (similarPattern) {
|
|
270
|
-
// Update existing pattern
|
|
271
|
-
await this.updatePattern(similarPattern, pattern, record);
|
|
272
|
-
} else {
|
|
273
|
-
// Create new pattern entry
|
|
274
|
-
await this.createPattern(patternKey, pattern, record);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Analyze cross-pattern relationships
|
|
279
|
-
await this.analyzePatternRelationships(record.patterns);
|
|
280
|
-
|
|
281
|
-
// Update pattern rankings
|
|
282
|
-
await this.updatePatternRankings();
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Find similar pattern
|
|
287
|
-
*/
|
|
288
|
-
async findSimilarPattern(pattern) {
|
|
289
|
-
for (const [key, existingPattern] of this.patterns) {
|
|
290
|
-
const similarity = await this.calculatePatternSimilarity(pattern, existingPattern);
|
|
291
|
-
|
|
292
|
-
if (similarity >= this.similarityThreshold) {
|
|
293
|
-
return { key, pattern: existingPattern, similarity };
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return null;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Calculate pattern similarity
|
|
302
|
-
*/
|
|
303
|
-
async calculatePatternSimilarity(pattern1, pattern2) {
|
|
304
|
-
// Type must match
|
|
305
|
-
if (pattern1.type !== pattern2.type) return 0;
|
|
306
|
-
|
|
307
|
-
let similarity = 0;
|
|
308
|
-
let factors = 0;
|
|
309
|
-
|
|
310
|
-
// Type-specific similarity calculation
|
|
311
|
-
switch (pattern1.type) {
|
|
312
|
-
case 'code_transformation':
|
|
313
|
-
similarity += this.calculateCodeSimilarity(pattern1, pattern2) * 0.7;
|
|
314
|
-
similarity += this.calculateContextSimilarity(pattern1.context, pattern2.context) * 0.3;
|
|
315
|
-
factors = 1;
|
|
316
|
-
break;
|
|
317
|
-
|
|
318
|
-
case 'structural':
|
|
319
|
-
similarity += this.calculateStructuralSimilarity(pattern1.pattern, pattern2.pattern) * 0.6;
|
|
320
|
-
similarity += this.calculateBenefitSimilarity(pattern1.benefits, pattern2.benefits) * 0.4;
|
|
321
|
-
factors = 1;
|
|
322
|
-
break;
|
|
323
|
-
|
|
324
|
-
case 'refactoring':
|
|
325
|
-
similarity += this.calculateRefactoringSimilarity(pattern1, pattern2);
|
|
326
|
-
factors = 1;
|
|
327
|
-
break;
|
|
328
|
-
|
|
329
|
-
case 'performance':
|
|
330
|
-
similarity += this.calculatePerformanceSimilarity(pattern1, pattern2);
|
|
331
|
-
factors = 1;
|
|
332
|
-
break;
|
|
333
|
-
|
|
334
|
-
default:
|
|
335
|
-
// Generic similarity based on pattern structure
|
|
336
|
-
similarity = this.calculateGenericSimilarity(pattern1, pattern2);
|
|
337
|
-
factors = 1;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return factors > 0 ? similarity / factors : 0;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Calculate code similarity
|
|
345
|
-
*/
|
|
346
|
-
calculateCodeSimilarity(pattern1, pattern2) {
|
|
347
|
-
const from1 = this.tokenizeCode(pattern1.from);
|
|
348
|
-
const from2 = this.tokenizeCode(pattern2.from);
|
|
349
|
-
const to1 = this.tokenizeCode(pattern1.to);
|
|
350
|
-
const to2 = this.tokenizeCode(pattern2.to);
|
|
351
|
-
|
|
352
|
-
const fromSimilarity = this.calculateTokenSimilarity(from1, from2);
|
|
353
|
-
const toSimilarity = this.calculateTokenSimilarity(to1, to2);
|
|
354
|
-
|
|
355
|
-
return (fromSimilarity + toSimilarity) / 2;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Tokenize code for comparison
|
|
360
|
-
*/
|
|
361
|
-
tokenizeCode(code) {
|
|
362
|
-
if (!code) return [];
|
|
363
|
-
|
|
364
|
-
// Simple tokenization - can be enhanced with proper AST parsing
|
|
365
|
-
return code
|
|
366
|
-
.replace(/\s+/g, ' ')
|
|
367
|
-
.replace(/[{}();,]/g, ' $& ')
|
|
368
|
-
.split(/\s+/)
|
|
369
|
-
.filter(token => token.length > 0);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Calculate token similarity
|
|
374
|
-
*/
|
|
375
|
-
calculateTokenSimilarity(tokens1, tokens2) {
|
|
376
|
-
const set1 = new Set(tokens1);
|
|
377
|
-
const set2 = new Set(tokens2);
|
|
378
|
-
|
|
379
|
-
const intersection = new Set([...set1].filter(x => set2.has(x)));
|
|
380
|
-
const union = new Set([...set1, ...set2]);
|
|
381
|
-
|
|
382
|
-
return union.size > 0 ? intersection.size / union.size : 0;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* Update existing pattern
|
|
387
|
-
*/
|
|
388
|
-
async updatePattern(similarPattern, newPattern, record) {
|
|
389
|
-
const existingPattern = similarPattern.pattern;
|
|
390
|
-
|
|
391
|
-
// Update occurrence count
|
|
392
|
-
existingPattern.occurrences = (existingPattern.occurrences || 0) + 1;
|
|
393
|
-
|
|
394
|
-
// Update success rate
|
|
395
|
-
existingPattern.successCount = (existingPattern.successCount || 0) + 1;
|
|
396
|
-
existingPattern.successRate = existingPattern.successCount / existingPattern.occurrences;
|
|
397
|
-
|
|
398
|
-
// Merge benefits/improvements
|
|
399
|
-
if (newPattern.benefits) {
|
|
400
|
-
existingPattern.aggregatedBenefits = this.aggregateBenefits(
|
|
401
|
-
existingPattern.aggregatedBenefits || {},
|
|
402
|
-
newPattern.benefits
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Add to usage history
|
|
407
|
-
if (!existingPattern.usageHistory) {
|
|
408
|
-
existingPattern.usageHistory = [];
|
|
409
|
-
}
|
|
410
|
-
existingPattern.usageHistory.push({
|
|
411
|
-
recordId: record.id,
|
|
412
|
-
timestamp: record.timestamp,
|
|
413
|
-
author: record.metadata.author,
|
|
414
|
-
outcomes: record.outcomes
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
// Update confidence score
|
|
418
|
-
existingPattern.confidence = this.calculatePatternConfidence(existingPattern);
|
|
419
|
-
|
|
420
|
-
// Check if pattern should be promoted
|
|
421
|
-
if (existingPattern.occurrences >= this.learningThreshold && existingPattern.confidence > 0.8) {
|
|
422
|
-
existingPattern.status = 'learned';
|
|
423
|
-
existingPattern.learnedAt = new Date().toISOString();
|
|
424
|
-
|
|
425
|
-
console.log(chalk.green(`✅ Pattern promoted to learned: ${similarPattern.key}`));
|
|
426
|
-
this.emit('pattern_learned', existingPattern);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Create new pattern
|
|
432
|
-
*/
|
|
433
|
-
async createPattern(key, pattern, record) {
|
|
434
|
-
const newPattern = {
|
|
435
|
-
...pattern,
|
|
436
|
-
key: key,
|
|
437
|
-
occurrences: 1,
|
|
438
|
-
successCount: 1,
|
|
439
|
-
successRate: 1.0,
|
|
440
|
-
firstSeen: record.timestamp,
|
|
441
|
-
lastSeen: record.timestamp,
|
|
442
|
-
status: 'candidate',
|
|
443
|
-
confidence: 0.3, // Initial low confidence
|
|
444
|
-
usageHistory: [{
|
|
445
|
-
recordId: record.id,
|
|
446
|
-
timestamp: record.timestamp,
|
|
447
|
-
author: record.metadata.author,
|
|
448
|
-
outcomes: record.outcomes
|
|
449
|
-
}]
|
|
450
|
-
};
|
|
451
|
-
|
|
452
|
-
this.patterns.set(key, newPattern);
|
|
453
|
-
|
|
454
|
-
console.log(chalk.gray(`New pattern candidate created: ${key}`));
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Calculate pattern confidence
|
|
459
|
-
*/
|
|
460
|
-
calculatePatternConfidence(pattern) {
|
|
461
|
-
let confidence = 0;
|
|
462
|
-
|
|
463
|
-
// Occurrence factor (up to 0.3)
|
|
464
|
-
const occurrenceFactor = Math.min(pattern.occurrences / 10, 0.3);
|
|
465
|
-
confidence += occurrenceFactor;
|
|
466
|
-
|
|
467
|
-
// Success rate factor (up to 0.4)
|
|
468
|
-
confidence += pattern.successRate * 0.4;
|
|
469
|
-
|
|
470
|
-
// Consistency factor (up to 0.2)
|
|
471
|
-
const consistencyFactor = this.calculateConsistencyFactor(pattern.usageHistory);
|
|
472
|
-
confidence += consistencyFactor * 0.2;
|
|
473
|
-
|
|
474
|
-
// Recency factor (up to 0.1)
|
|
475
|
-
const recencyFactor = this.calculateRecencyFactor(pattern.lastSeen);
|
|
476
|
-
confidence += recencyFactor * 0.1;
|
|
477
|
-
|
|
478
|
-
return Math.min(confidence, 1.0);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
/**
|
|
482
|
-
* Get pattern suggestions for modification
|
|
483
|
-
*/
|
|
484
|
-
async getPatternSuggestions(_context) {
|
|
485
|
-
const suggestions = [];
|
|
486
|
-
|
|
487
|
-
// Filter applicable patterns
|
|
488
|
-
const applicablePatterns = Array.from(this.patterns.values()).filter(pattern => {
|
|
489
|
-
return pattern.status === 'learned' &&
|
|
490
|
-
this.isPatternApplicable(pattern, context) &&
|
|
491
|
-
pattern.confidence > 0.7;
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
// Sort by relevance and confidence
|
|
495
|
-
applicablePatterns.sort((a, b) => {
|
|
496
|
-
const relevanceA = this.calculateRelevance(a, context);
|
|
497
|
-
const relevanceB = this.calculateRelevance(b, context);
|
|
498
|
-
return (relevanceB * b.confidence) - (relevanceA * a.confidence);
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
// Create suggestions
|
|
502
|
-
for (const pattern of applicablePatterns.slice(0, 5)) {
|
|
503
|
-
suggestions.push({
|
|
504
|
-
pattern: pattern,
|
|
505
|
-
relevance: this.calculateRelevance(pattern, context),
|
|
506
|
-
confidence: pattern.confidence,
|
|
507
|
-
expectedBenefits: pattern.aggregatedBenefits || {},
|
|
508
|
-
applicationGuide: await this.generateApplicationGuide(pattern, context),
|
|
509
|
-
examples: this.getPatternExamples(pattern)
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
return suggestions;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Check if pattern is applicable
|
|
518
|
-
*/
|
|
519
|
-
isPatternApplicable(pattern, context) {
|
|
520
|
-
// Check component type compatibility
|
|
521
|
-
if (pattern.componentType && context.componentType) {
|
|
522
|
-
if (pattern.componentType !== context.componentType && pattern.componentType !== 'any') {
|
|
523
|
-
return false;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Check context requirements
|
|
528
|
-
if (pattern.requiredContext) {
|
|
529
|
-
for (const requirement of pattern.requiredContext) {
|
|
530
|
-
if (!this.meetsContextRequirement(_context, requirement)) {
|
|
531
|
-
return false;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Check applicability conditions
|
|
537
|
-
if (pattern.applicableContexts) {
|
|
538
|
-
return pattern.applicableContexts.some(ctx =>
|
|
539
|
-
this.matchesContext(_context, ctx)
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
return true;
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
/**
|
|
547
|
-
* Generate application guide
|
|
548
|
-
*/
|
|
549
|
-
async generateApplicationGuide(pattern, context) {
|
|
550
|
-
const guide = {
|
|
551
|
-
steps: [],
|
|
552
|
-
preconditions: [],
|
|
553
|
-
expectedOutcome: {},
|
|
554
|
-
risks: [],
|
|
555
|
-
alternatives: []
|
|
556
|
-
};
|
|
557
|
-
|
|
558
|
-
// Generate steps based on pattern type
|
|
559
|
-
switch (pattern.type) {
|
|
560
|
-
case 'code_transformation':
|
|
561
|
-
guide.steps = this.generateCodeTransformationSteps(pattern, context);
|
|
562
|
-
break;
|
|
563
|
-
case 'refactoring':
|
|
564
|
-
guide.steps = pattern.steps || [];
|
|
565
|
-
guide.preconditions = pattern.triggers || [];
|
|
566
|
-
break;
|
|
567
|
-
case 'performance':
|
|
568
|
-
guide.steps = this.generatePerformanceOptimizationSteps(pattern, context);
|
|
569
|
-
guide.expectedOutcome = pattern.metrics;
|
|
570
|
-
break;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Add general guidance
|
|
574
|
-
guide.confidence = `${Math.round(pattern.confidence * 100)}%`;
|
|
575
|
-
guide.successRate = `${Math.round(pattern.successRate * 100)}%`;
|
|
576
|
-
guide.usageCount = pattern.occurrences;
|
|
577
|
-
|
|
578
|
-
return guide;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
/**
|
|
582
|
-
* Analyze pattern relationships
|
|
583
|
-
*/
|
|
584
|
-
async analyzePatternRelationships(patterns) {
|
|
585
|
-
// Find patterns that commonly occur together
|
|
586
|
-
const coOccurrences = new Map();
|
|
587
|
-
|
|
588
|
-
for (let i = 0; i < patterns.length; i++) {
|
|
589
|
-
for (let j = i + 1; j < patterns.length; j++) {
|
|
590
|
-
const key = this.generateRelationshipKey(patterns[i], patterns[j]);
|
|
591
|
-
const existing = coOccurrences.get(key) || { count: 0, patterns: [] };
|
|
592
|
-
existing.count++;
|
|
593
|
-
existing.patterns = [patterns[i], patterns[j]];
|
|
594
|
-
coOccurrences.set(key, existing);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// Store significant relationships
|
|
599
|
-
for (const [key, relationship] of coOccurrences) {
|
|
600
|
-
if (relationship.count >= 2) {
|
|
601
|
-
await this.storePatternRelationship(key, relationship);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Get pattern analytics
|
|
608
|
-
*/
|
|
609
|
-
async getPatternAnalytics() {
|
|
610
|
-
const analytics = {
|
|
611
|
-
totalPatterns: this.patterns.size,
|
|
612
|
-
learnedPatterns: 0,
|
|
613
|
-
candidatePatterns: 0,
|
|
614
|
-
patternsByType: {},
|
|
615
|
-
topPatterns: [],
|
|
616
|
-
recentTrends: [],
|
|
617
|
-
effectivenessMetrics: {}
|
|
618
|
-
};
|
|
619
|
-
|
|
620
|
-
// Count patterns by status and type
|
|
621
|
-
for (const pattern of this.patterns.values()) {
|
|
622
|
-
if (pattern.status === 'learned') {
|
|
623
|
-
analytics.learnedPatterns++;
|
|
624
|
-
} else {
|
|
625
|
-
analytics.candidatePatterns++;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
analytics.patternsByType[pattern.type] =
|
|
629
|
-
(analytics.patternsByType[pattern.type] || 0) + 1;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Get top patterns by usage
|
|
633
|
-
const sortedPatterns = Array.from(this.patterns.values())
|
|
634
|
-
.sort((a, b) => b.occurrences - a.occurrences);
|
|
635
|
-
|
|
636
|
-
analytics.topPatterns = sortedPatterns.slice(0, 10).map(p => ({
|
|
637
|
-
key: p.key,
|
|
638
|
-
type: p.type,
|
|
639
|
-
occurrences: p.occurrences,
|
|
640
|
-
successRate: p.successRate,
|
|
641
|
-
confidence: p.confidence
|
|
642
|
-
}));
|
|
643
|
-
|
|
644
|
-
// Calculate effectiveness metrics
|
|
645
|
-
analytics.effectivenessMetrics = await this.calculateEffectivenessMetrics();
|
|
646
|
-
|
|
647
|
-
// Get recent trends
|
|
648
|
-
analytics.recentTrends = await this.analyzeRecentTrends();
|
|
649
|
-
|
|
650
|
-
return analytics;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* Calculate effectiveness metrics
|
|
655
|
-
*/
|
|
656
|
-
async calculateEffectivenessMetrics() {
|
|
657
|
-
const metrics = {
|
|
658
|
-
averageSuccessRate: 0,
|
|
659
|
-
averageConfidence: 0,
|
|
660
|
-
patternCoverage: 0,
|
|
661
|
-
learningRate: 0
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
const learnedPatterns = Array.from(this.patterns.values())
|
|
665
|
-
.filter(p => p.status === 'learned');
|
|
666
|
-
|
|
667
|
-
if (learnedPatterns.length > 0) {
|
|
668
|
-
metrics.averageSuccessRate = learnedPatterns.reduce((sum, p) =>
|
|
669
|
-
sum + p.successRate, 0) / learnedPatterns.length;
|
|
670
|
-
|
|
671
|
-
metrics.averageConfidence = learnedPatterns.reduce((sum, p) =>
|
|
672
|
-
sum + p.confidence, 0) / learnedPatterns.length;
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
// Calculate pattern coverage
|
|
676
|
-
const modificationTypes = new Set(this.modificationHistory.map(m => m.modificationType));
|
|
677
|
-
const coveredTypes = new Set(learnedPatterns.map(p => p.type));
|
|
678
|
-
metrics.patternCoverage = modificationTypes.size > 0 ?
|
|
679
|
-
coveredTypes.size / modificationTypes.size : 0;
|
|
680
|
-
|
|
681
|
-
// Calculate learning rate
|
|
682
|
-
const recentHistory = this.modificationHistory.slice(-20);
|
|
683
|
-
const recentLearned = recentHistory.filter(m =>
|
|
684
|
-
m.patterns.some(p => this.patterns.get(this.generatePatternKey(p))?.status === 'learned')
|
|
685
|
-
);
|
|
686
|
-
metrics.learningRate = recentHistory.length > 0 ?
|
|
687
|
-
recentLearned.length / recentHistory.length : 0;
|
|
688
|
-
|
|
689
|
-
return metrics;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
/**
|
|
693
|
-
* Helper methods
|
|
694
|
-
*/
|
|
695
|
-
|
|
696
|
-
normalizeCode(code) {
|
|
697
|
-
if (!code) return '';
|
|
698
|
-
return code.trim().replace(/\s+/g, ' ');
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
generatePatternKey(pattern) {
|
|
702
|
-
return `${pattern.type}:${pattern.subtype || 'default'}:${
|
|
703
|
-
crypto.createHash('md5').update(JSON.stringify(pattern)).digest('hex').substr(0, 8)
|
|
704
|
-
}`;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
aggregateBenefits(existing, newBenefits) {
|
|
708
|
-
const aggregated = { ...existing };
|
|
709
|
-
|
|
710
|
-
for (const [key, value] of Object.entries(newBenefits)) {
|
|
711
|
-
if (typeof value === 'number') {
|
|
712
|
-
aggregated[key] = (aggregated[key] || 0) + value;
|
|
713
|
-
aggregated[`${key}_avg`] = aggregated[key] / ((aggregated[`${key}_count`] || 0) + 1);
|
|
714
|
-
aggregated[`${key}_count`] = (aggregated[`${key}_count`] || 0) + 1;
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
return aggregated;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
calculateConsistencyFactor(usageHistory) {
|
|
722
|
-
if (usageHistory.length < 2) return 1.0;
|
|
723
|
-
|
|
724
|
-
// Check time intervals between uses
|
|
725
|
-
const intervals = [];
|
|
726
|
-
for (let i = 1; i < usageHistory.length; i++) {
|
|
727
|
-
const interval = new Date(usageHistory[i].timestamp) - new Date(usageHistory[i-1].timestamp);
|
|
728
|
-
intervals.push(interval);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// Calculate variance
|
|
732
|
-
const avgInterval = intervals.reduce((sum, i) => sum + i, 0) / intervals.length;
|
|
733
|
-
const variance = intervals.reduce((sum, i) => sum + Math.pow(i - avgInterval, 2), 0) / intervals.length;
|
|
734
|
-
const stdDev = Math.sqrt(variance);
|
|
735
|
-
|
|
736
|
-
// Lower variance = higher consistency
|
|
737
|
-
return 1 / (1 + stdDev / avgInterval);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
calculateRecencyFactor(lastSeen) {
|
|
741
|
-
const daysSinceLastSeen = (Date.now() - new Date(lastSeen).getTime()) / (1000 * 60 * 60 * 24);
|
|
742
|
-
return Math.max(0, 1 - daysSinceLastSeen / 30); // Decay over 30 days
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
calculateRelevance(pattern, context) {
|
|
746
|
-
let relevance = 0;
|
|
747
|
-
|
|
748
|
-
// Type match
|
|
749
|
-
if (pattern.type === context.modificationType) {
|
|
750
|
-
relevance += 0.3;
|
|
751
|
-
}
|
|
752
|
-
|
|
753
|
-
// Component type match
|
|
754
|
-
if (pattern.componentType === context.componentType) {
|
|
755
|
-
relevance += 0.2;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// Context similarity
|
|
759
|
-
if (pattern.context && context.currentContext) {
|
|
760
|
-
relevance += this.calculateContextSimilarity(pattern.context, context.currentContext) * 0.3;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
// Goal alignment
|
|
764
|
-
if (pattern.benefits && context.goals) {
|
|
765
|
-
relevance += this.calculateGoalAlignment(pattern.benefits, context.goals) * 0.2;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
return relevance;
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
getPatternExamples(pattern) {
|
|
772
|
-
return pattern.usageHistory
|
|
773
|
-
.slice(-3)
|
|
774
|
-
.map(usage => ({
|
|
775
|
-
recordId: usage.recordId,
|
|
776
|
-
timestamp: usage.timestamp,
|
|
777
|
-
author: usage.author,
|
|
778
|
-
outcomes: usage.outcomes
|
|
779
|
-
}));
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
/**
|
|
783
|
-
* Save and load methods
|
|
784
|
-
*/
|
|
785
|
-
|
|
786
|
-
async saveHistory() {
|
|
787
|
-
await fs.writeFile(
|
|
788
|
-
this.historyFile,
|
|
789
|
-
JSON.stringify(this.modificationHistory, null, 2)
|
|
790
|
-
);
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
async loadHistory() {
|
|
794
|
-
try {
|
|
795
|
-
const content = await fs.readFile(this.historyFile, 'utf-8');
|
|
796
|
-
this.modificationHistory = JSON.parse(content);
|
|
797
|
-
} catch (_error) {
|
|
798
|
-
// No history file yet
|
|
799
|
-
this.modificationHistory = [];
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
async savePatterns() {
|
|
804
|
-
const patternsArray = Array.from(this.patterns.entries()).map(([key, pattern]) => ({
|
|
805
|
-
key,
|
|
806
|
-
...pattern
|
|
807
|
-
}));
|
|
808
|
-
|
|
809
|
-
await fs.writeFile(
|
|
810
|
-
this.patternsFile,
|
|
811
|
-
JSON.stringify(patternsArray, null, 2)
|
|
812
|
-
);
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
async loadPatterns() {
|
|
816
|
-
try {
|
|
817
|
-
const content = await fs.readFile(this.patternsFile, 'utf-8');
|
|
818
|
-
const patternsArray = JSON.parse(content);
|
|
819
|
-
|
|
820
|
-
this.patterns.clear();
|
|
821
|
-
for (const pattern of patternsArray) {
|
|
822
|
-
this.patterns.set(pattern.key, pattern);
|
|
823
|
-
}
|
|
824
|
-
} catch (_error) {
|
|
825
|
-
// No patterns file yet
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
/**
|
|
830
|
-
* Check learning threshold
|
|
831
|
-
*/
|
|
832
|
-
async checkLearningThreshold(patterns) {
|
|
833
|
-
let learnedCount = 0;
|
|
834
|
-
|
|
835
|
-
for (const pattern of patterns) {
|
|
836
|
-
const key = this.generatePatternKey(pattern);
|
|
837
|
-
const existing = this.patterns.get(key);
|
|
838
|
-
|
|
839
|
-
if (existing && existing.occurrences >= this.learningThreshold) {
|
|
840
|
-
learnedCount++;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
return learnedCount > 0;
|
|
845
|
-
}
|
|
846
|
-
|
|
847
|
-
calculateContextSimilarity(context1, context2) {
|
|
848
|
-
// Simple context similarity - can be enhanced
|
|
849
|
-
if (!context1 || !context2) return 0;
|
|
850
|
-
|
|
851
|
-
const keys1 = Object.keys(context1);
|
|
852
|
-
const keys2 = Object.keys(context2);
|
|
853
|
-
const commonKeys = keys1.filter(k => keys2.includes(k));
|
|
854
|
-
|
|
855
|
-
if (commonKeys.length === 0) return 0;
|
|
856
|
-
|
|
857
|
-
let similarity = commonKeys.length / Math.max(keys1.length, keys2.length);
|
|
858
|
-
|
|
859
|
-
// Check value similarity for common keys
|
|
860
|
-
for (const key of commonKeys) {
|
|
861
|
-
if (context1[key] === context2[key]) {
|
|
862
|
-
similarity += 0.1;
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
return Math.min(similarity, 1.0);
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
calculateStructuralSimilarity(struct1, struct2) {
|
|
870
|
-
// Compare structural patterns
|
|
871
|
-
if (!struct1 || !struct2) return 0;
|
|
872
|
-
|
|
873
|
-
const before1 = JSON.stringify(struct1.before);
|
|
874
|
-
const before2 = JSON.stringify(struct2.before);
|
|
875
|
-
const after1 = JSON.stringify(struct1.after);
|
|
876
|
-
const after2 = JSON.stringify(struct2.after);
|
|
877
|
-
|
|
878
|
-
const beforeSim = before1 === before2 ? 1 : 0.5;
|
|
879
|
-
const afterSim = after1 === after2 ? 1 : 0.5;
|
|
880
|
-
|
|
881
|
-
return (beforeSim + afterSim) / 2;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
calculateBenefitSimilarity(benefits1, benefits2) {
|
|
885
|
-
if (!benefits1 || !benefits2) return 0;
|
|
886
|
-
|
|
887
|
-
const keys1 = Object.keys(benefits1);
|
|
888
|
-
const keys2 = Object.keys(benefits2);
|
|
889
|
-
const allKeys = new Set([...keys1, ...keys2]);
|
|
890
|
-
|
|
891
|
-
let similarity = 0;
|
|
892
|
-
for (const key of allKeys) {
|
|
893
|
-
if (benefits1[key] && benefits2[key]) {
|
|
894
|
-
// Both have the benefit
|
|
895
|
-
similarity += 1;
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
return allKeys.size > 0 ? similarity / allKeys.size : 0;
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
calculateRefactoringSimilarity(refactor1, refactor2) {
|
|
903
|
-
if (refactor1.refactoringType !== refactor2.refactoringType) return 0;
|
|
904
|
-
|
|
905
|
-
let similarity = 0.5; // Base similarity for same type
|
|
906
|
-
|
|
907
|
-
// Compare triggers
|
|
908
|
-
if (refactor1.triggers && refactor2.triggers) {
|
|
909
|
-
const commonTriggers = refactor1.triggers.filter(t =>
|
|
910
|
-
refactor2.triggers.includes(t)
|
|
911
|
-
);
|
|
912
|
-
similarity += commonTriggers.length / Math.max(refactor1.triggers.length, refactor2.triggers.length) * 0.3;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
// Compare steps
|
|
916
|
-
if (refactor1.steps && refactor2.steps) {
|
|
917
|
-
const stepSimilarity = Math.min(refactor1.steps.length, refactor2.steps.length) /
|
|
918
|
-
Math.max(refactor1.steps.length, refactor2.steps.length);
|
|
919
|
-
similarity += stepSimilarity * 0.2;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
return similarity;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
calculatePerformanceSimilarity(perf1, perf2) {
|
|
926
|
-
if (perf1.optimizationType !== perf2.optimizationType) return 0;
|
|
927
|
-
|
|
928
|
-
let similarity = 0.4; // Base similarity for same type
|
|
929
|
-
|
|
930
|
-
if (perf1.technique === perf2.technique) {
|
|
931
|
-
similarity += 0.3;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
// Compare applicable contexts
|
|
935
|
-
if (perf1.applicableContexts && perf2.applicableContexts) {
|
|
936
|
-
const commonContexts = perf1.applicableContexts.filter(c =>
|
|
937
|
-
perf2.applicableContexts.includes(c)
|
|
938
|
-
);
|
|
939
|
-
similarity += commonContexts.length / Math.max(perf1.applicableContexts.length, perf2.applicableContexts.length) * 0.3;
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
return similarity;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
calculateGenericSimilarity(pattern1, pattern2) {
|
|
946
|
-
// Generic JSON similarity
|
|
947
|
-
const json1 = JSON.stringify(pattern1);
|
|
948
|
-
const json2 = JSON.stringify(pattern2);
|
|
949
|
-
|
|
950
|
-
if (json1 === json2) return 1.0;
|
|
951
|
-
|
|
952
|
-
// Calculate Levenshtein distance ratio
|
|
953
|
-
const distance = this.levenshteinDistance(json1, json2);
|
|
954
|
-
const maxLength = Math.max(json1.length, json2.length);
|
|
955
|
-
|
|
956
|
-
return 1 - (distance / maxLength);
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
levenshteinDistance(str1, str2) {
|
|
960
|
-
const matrix = [];
|
|
961
|
-
|
|
962
|
-
for (let i = 0; i <= str2.length; i++) {
|
|
963
|
-
matrix[i] = [i];
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
for (let j = 0; j <= str1.length; j++) {
|
|
967
|
-
matrix[0][j] = j;
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
for (let i = 1; i <= str2.length; i++) {
|
|
971
|
-
for (let j = 1; j <= str1.length; j++) {
|
|
972
|
-
if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
|
|
973
|
-
matrix[i][j] = matrix[i - 1][j - 1];
|
|
974
|
-
} else {
|
|
975
|
-
matrix[i][j] = Math.min(
|
|
976
|
-
matrix[i - 1][j - 1] + 1,
|
|
977
|
-
matrix[i][j - 1] + 1,
|
|
978
|
-
matrix[i - 1][j] + 1
|
|
979
|
-
);
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
return matrix[str2.length][str1.length];
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
meetsContextRequirement(_context, requirement) {
|
|
988
|
-
// Check if context meets specific requirement
|
|
989
|
-
if (requirement.type === 'has_property') {
|
|
990
|
-
return context[requirement.property] !== undefined;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
if (requirement.type === 'property_value') {
|
|
994
|
-
return context[requirement.property] === requirement.value;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
if (requirement.type === 'property_range') {
|
|
998
|
-
const value = context[requirement.property];
|
|
999
|
-
return value >= requirement.min && value <= requirement.max;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
return true;
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
matchesContext(_context, patternContext) {
|
|
1006
|
-
// Check if contexts match
|
|
1007
|
-
for (const [key, value] of Object.entries(patternContext)) {
|
|
1008
|
-
if (context[key] !== value) {
|
|
1009
|
-
return false;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
return true;
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
generateCodeTransformationSteps(pattern, context) {
|
|
1016
|
-
const steps = [];
|
|
1017
|
-
|
|
1018
|
-
steps.push({
|
|
1019
|
-
step: 1,
|
|
1020
|
-
action: 'Identify target code pattern',
|
|
1021
|
-
description: `Look for code matching: ${pattern.from}`,
|
|
1022
|
-
validation: 'Ensure code structure matches the pattern'
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
steps.push({
|
|
1026
|
-
step: 2,
|
|
1027
|
-
action: 'Apply transformation',
|
|
1028
|
-
description: `Transform to: ${pattern.to}`,
|
|
1029
|
-
validation: 'Verify transformation preserves functionality'
|
|
1030
|
-
});
|
|
1031
|
-
|
|
1032
|
-
if (pattern.context) {
|
|
1033
|
-
steps.push({
|
|
1034
|
-
step: 3,
|
|
1035
|
-
action: 'Validate context',
|
|
1036
|
-
description: 'Ensure transformation is appropriate for context',
|
|
1037
|
-
validation: pattern.context
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
steps.push({
|
|
1042
|
-
step: 4,
|
|
1043
|
-
action: 'Test changes',
|
|
1044
|
-
description: 'Run tests to ensure no regression',
|
|
1045
|
-
validation: 'All tests pass'
|
|
1046
|
-
});
|
|
1047
|
-
|
|
1048
|
-
return steps;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
generatePerformanceOptimizationSteps(pattern, context) {
|
|
1052
|
-
const steps = [];
|
|
1053
|
-
|
|
1054
|
-
steps.push({
|
|
1055
|
-
step: 1,
|
|
1056
|
-
action: 'Measure baseline performance',
|
|
1057
|
-
description: 'Capture current performance metrics',
|
|
1058
|
-
validation: 'Baseline metrics recorded'
|
|
1059
|
-
});
|
|
1060
|
-
|
|
1061
|
-
steps.push({
|
|
1062
|
-
step: 2,
|
|
1063
|
-
action: `Apply ${pattern.technique} optimization`,
|
|
1064
|
-
description: pattern.description || 'Apply performance optimization technique',
|
|
1065
|
-
validation: 'Optimization applied correctly'
|
|
1066
|
-
});
|
|
1067
|
-
|
|
1068
|
-
steps.push({
|
|
1069
|
-
step: 3,
|
|
1070
|
-
action: 'Measure improved performance',
|
|
1071
|
-
description: 'Capture post-optimization metrics',
|
|
1072
|
-
validation: `Expected improvement: ${pattern.metrics.improvement}%`
|
|
1073
|
-
});
|
|
1074
|
-
|
|
1075
|
-
steps.push({
|
|
1076
|
-
step: 4,
|
|
1077
|
-
action: 'Validate functionality',
|
|
1078
|
-
description: 'Ensure optimization didn\'t break functionality',
|
|
1079
|
-
validation: 'All tests pass'
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
return steps;
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
generateRelationshipKey(pattern1, pattern2) {
|
|
1086
|
-
const types = [pattern1.type, pattern2.type].sort();
|
|
1087
|
-
return `rel:${types.join(':')}`;
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
async storePatternRelationship(key, relationship) {
|
|
1091
|
-
// Store pattern relationships for future analysis
|
|
1092
|
-
const relationshipsFile = path.join(this.patternsDir, 'relationships.json');
|
|
1093
|
-
|
|
1094
|
-
let relationships = {};
|
|
1095
|
-
try {
|
|
1096
|
-
const content = await fs.readFile(relationshipsFile, 'utf-8');
|
|
1097
|
-
relationships = JSON.parse(content);
|
|
1098
|
-
} catch (_error) {
|
|
1099
|
-
// No relationships file yet
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
relationships[key] = relationship;
|
|
1103
|
-
|
|
1104
|
-
await fs.writeFile(relationshipsFile, JSON.stringify(relationships, null, 2));
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
async analyzeRecentTrends() {
|
|
1108
|
-
const recentModifications = this.modificationHistory.slice(-30);
|
|
1109
|
-
const trends = {
|
|
1110
|
-
emergingPatterns: [],
|
|
1111
|
-
decliningPatterns: [],
|
|
1112
|
-
stablePatterns: []
|
|
1113
|
-
};
|
|
1114
|
-
|
|
1115
|
-
// Analyze pattern usage over time
|
|
1116
|
-
const patternUsage = new Map();
|
|
1117
|
-
|
|
1118
|
-
for (const mod of recentModifications) {
|
|
1119
|
-
for (const pattern of mod.patterns) {
|
|
1120
|
-
const key = this.generatePatternKey(pattern);
|
|
1121
|
-
const usage = patternUsage.get(key) || { count: 0, recent: 0 };
|
|
1122
|
-
usage.count++;
|
|
1123
|
-
|
|
1124
|
-
// Check if in last 10 modifications
|
|
1125
|
-
const modIndex = recentModifications.indexOf(mod);
|
|
1126
|
-
if (modIndex >= recentModifications.length - 10) {
|
|
1127
|
-
usage.recent++;
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
patternUsage.set(key, usage);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
// Classify patterns
|
|
1135
|
-
for (const [key, usage] of patternUsage) {
|
|
1136
|
-
const pattern = this.patterns.get(key);
|
|
1137
|
-
if (!pattern) continue;
|
|
1138
|
-
|
|
1139
|
-
const recentRatio = usage.recent / usage.count;
|
|
1140
|
-
|
|
1141
|
-
if (recentRatio > 0.6) {
|
|
1142
|
-
trends.emergingPatterns.push({
|
|
1143
|
-
key: key,
|
|
1144
|
-
type: pattern.type,
|
|
1145
|
-
trend: 'emerging',
|
|
1146
|
-
usage: usage
|
|
1147
|
-
});
|
|
1148
|
-
} else if (recentRatio < 0.2) {
|
|
1149
|
-
trends.decliningPatterns.push({
|
|
1150
|
-
key: key,
|
|
1151
|
-
type: pattern.type,
|
|
1152
|
-
trend: 'declining',
|
|
1153
|
-
usage: usage
|
|
1154
|
-
});
|
|
1155
|
-
} else {
|
|
1156
|
-
trends.stablePatterns.push({
|
|
1157
|
-
key: key,
|
|
1158
|
-
type: pattern.type,
|
|
1159
|
-
trend: 'stable',
|
|
1160
|
-
usage: usage
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
return trends;
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
calculateGoalAlignment(benefits, goals) {
|
|
1169
|
-
if (!benefits || !goals) return 0;
|
|
1170
|
-
|
|
1171
|
-
let alignment = 0;
|
|
1172
|
-
let _matchedGoals = 0;
|
|
1173
|
-
|
|
1174
|
-
for (const goal of goals) {
|
|
1175
|
-
if (goal.type === 'performance' && benefits.performanceImprovement) {
|
|
1176
|
-
alignment += benefits.performanceImprovement > goal.target ? 1 : 0.5;
|
|
1177
|
-
matchedGoals++;
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
if (goal.type === 'maintainability' && benefits.maintainability) {
|
|
1181
|
-
alignment += benefits.maintainability > goal.target ? 1 : 0.5;
|
|
1182
|
-
matchedGoals++;
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
if (goal.type === 'testability' && benefits.testability) {
|
|
1186
|
-
alignment += benefits.testability > goal.target ? 1 : 0.5;
|
|
1187
|
-
matchedGoals++;
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
return goals.length > 0 ? alignment / goals.length : 0;
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
async updatePatternRankings() {
|
|
1195
|
-
// Update pattern rankings based on multiple factors
|
|
1196
|
-
for (const pattern of this.patterns.values()) {
|
|
1197
|
-
pattern.ranking = this.calculatePatternRanking(pattern);
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
// Save updated patterns
|
|
1201
|
-
await this.savePatterns();
|
|
1202
|
-
}
|
|
1203
|
-
|
|
1204
|
-
calculatePatternRanking(pattern) {
|
|
1205
|
-
let ranking = 0;
|
|
1206
|
-
|
|
1207
|
-
// Success rate (40%)
|
|
1208
|
-
ranking += pattern.successRate * 40;
|
|
1209
|
-
|
|
1210
|
-
// Usage frequency (30%)
|
|
1211
|
-
const usageScore = Math.min(pattern.occurrences / 20, 1);
|
|
1212
|
-
ranking += usageScore * 30;
|
|
1213
|
-
|
|
1214
|
-
// Confidence (20%)
|
|
1215
|
-
ranking += pattern.confidence * 20;
|
|
1216
|
-
|
|
1217
|
-
// Recency (10%)
|
|
1218
|
-
const recencyScore = this.calculateRecencyFactor(pattern.lastSeen);
|
|
1219
|
-
ranking += recencyScore * 10;
|
|
1220
|
-
|
|
1221
|
-
return ranking;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1
|
+
const fs = require('fs').promises;
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const EventEmitter = require('events');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Pattern learning system for successful modifications
|
|
8
|
+
* Learns from successful modifications to suggest improvements and automate common patterns
|
|
9
|
+
*/
|
|
10
|
+
class PatternLearner extends EventEmitter {
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
super();
|
|
13
|
+
this.rootPath = options.rootPath || process.cwd();
|
|
14
|
+
this.patternsDir = path.join(this.rootPath, '.aios', 'patterns');
|
|
15
|
+
this.historyFile = path.join(this.patternsDir, 'modification_history.json');
|
|
16
|
+
this.patternsFile = path.join(this.patternsDir, 'learned_patterns.json');
|
|
17
|
+
this.patterns = new Map();
|
|
18
|
+
this.modificationHistory = [];
|
|
19
|
+
this.learningThreshold = options.learningThreshold || 3; // Minimum occurrences to learn pattern
|
|
20
|
+
this.similarityThreshold = options.similarityThreshold || 0.8; // 80% similarity
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Initialize pattern learner
|
|
25
|
+
*/
|
|
26
|
+
async initialize() {
|
|
27
|
+
await fs.mkdir(this.patternsDir, { recursive: true });
|
|
28
|
+
await this.loadHistory();
|
|
29
|
+
await this.loadPatterns();
|
|
30
|
+
console.log(chalk.green('✅ Pattern learner initialized'));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Record successful modification for learning
|
|
35
|
+
*/
|
|
36
|
+
async recordSuccessfulModification(modification) {
|
|
37
|
+
const record = {
|
|
38
|
+
id: `mod-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`,
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
componentType: modification.componentType,
|
|
41
|
+
modificationType: modification.modificationType,
|
|
42
|
+
patterns: await this.extractPatterns(modification),
|
|
43
|
+
outcomes: {
|
|
44
|
+
success: true,
|
|
45
|
+
metrics: modification.metrics || {},
|
|
46
|
+
improvements: modification.improvements || []
|
|
47
|
+
},
|
|
48
|
+
metadata: {
|
|
49
|
+
author: modification.author || process.env.USER || 'unknown',
|
|
50
|
+
duration: modification.duration,
|
|
51
|
+
complexity: modification.complexity
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Add to history
|
|
56
|
+
this.modificationHistory.push(record);
|
|
57
|
+
|
|
58
|
+
// Learn from this modification
|
|
59
|
+
await this.learnFromModification(record);
|
|
60
|
+
|
|
61
|
+
// Save updated history
|
|
62
|
+
await this.saveHistory();
|
|
63
|
+
|
|
64
|
+
// Emit event for real-time learning
|
|
65
|
+
this.emit('modification_recorded', record);
|
|
66
|
+
|
|
67
|
+
console.log(chalk.green(`✅ Recorded successful modification: ${record.id}`));
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
recordId: record.id,
|
|
71
|
+
patternsExtracted: record.patterns.length,
|
|
72
|
+
learningTriggered: await this.checkLearningThreshold(record.patterns)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Extract patterns from modification
|
|
78
|
+
*/
|
|
79
|
+
async extractPatterns(modification) {
|
|
80
|
+
const patterns = [];
|
|
81
|
+
|
|
82
|
+
// Code change patterns
|
|
83
|
+
if (modification.codeChanges) {
|
|
84
|
+
patterns.push(...this.extractCodePatterns(modification.codeChanges));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Structural patterns
|
|
88
|
+
if (modification.structuralChanges) {
|
|
89
|
+
patterns.push(...this.extractStructuralPatterns(modification.structuralChanges));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Refactoring patterns
|
|
93
|
+
if (modification.refactoringType) {
|
|
94
|
+
patterns.push(...this.extractRefactoringPatterns(modification));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Dependency patterns
|
|
98
|
+
if (modification.dependencyChanges) {
|
|
99
|
+
patterns.push(...this.extractDependencyPatterns(modification.dependencyChanges));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Performance patterns
|
|
103
|
+
if (modification.performanceImprovements) {
|
|
104
|
+
patterns.push(...this.extractPerformancePatterns(modification.performanceImprovements));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return patterns;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Extract code change patterns
|
|
112
|
+
*/
|
|
113
|
+
extractCodePatterns(codeChanges) {
|
|
114
|
+
const patterns = [];
|
|
115
|
+
|
|
116
|
+
for (const change of codeChanges) {
|
|
117
|
+
// Function transformation patterns
|
|
118
|
+
if (change.type === 'function_transformation') {
|
|
119
|
+
patterns.push({
|
|
120
|
+
type: 'code_transformation',
|
|
121
|
+
subtype: 'function',
|
|
122
|
+
from: this.normalizeCode(change.before),
|
|
123
|
+
to: this.normalizeCode(change.after),
|
|
124
|
+
context: change.context,
|
|
125
|
+
benefits: change.benefits || []
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Error handling patterns
|
|
130
|
+
if (change.type === 'error_handling') {
|
|
131
|
+
patterns.push({
|
|
132
|
+
type: 'error_handling',
|
|
133
|
+
subtype: change.handlingType,
|
|
134
|
+
pattern: change.pattern,
|
|
135
|
+
improvement: change.improvement
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Async/await patterns
|
|
140
|
+
if (change.type === 'async_transformation') {
|
|
141
|
+
patterns.push({
|
|
142
|
+
type: 'async_pattern',
|
|
143
|
+
from: change.callbackPattern,
|
|
144
|
+
to: change.asyncPattern,
|
|
145
|
+
complexity_reduction: change.complexityReduction
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// API usage patterns
|
|
150
|
+
if (change.type === 'api_improvement') {
|
|
151
|
+
patterns.push({
|
|
152
|
+
type: 'api_usage',
|
|
153
|
+
oldPattern: change.oldUsage,
|
|
154
|
+
newPattern: change.newUsage,
|
|
155
|
+
benefits: change.benefits
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return patterns;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Extract structural patterns
|
|
165
|
+
*/
|
|
166
|
+
extractStructuralPatterns(structuralChanges) {
|
|
167
|
+
const patterns = [];
|
|
168
|
+
|
|
169
|
+
for (const change of structuralChanges) {
|
|
170
|
+
patterns.push({
|
|
171
|
+
type: 'structural',
|
|
172
|
+
changeType: change.type,
|
|
173
|
+
pattern: {
|
|
174
|
+
before: change.beforeStructure,
|
|
175
|
+
after: change.afterStructure
|
|
176
|
+
},
|
|
177
|
+
benefits: {
|
|
178
|
+
modularity: change.modularityImprovement || 0,
|
|
179
|
+
maintainability: change.maintainabilityImprovement || 0,
|
|
180
|
+
testability: change.testabilityImprovement || 0
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return patterns;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Extract refactoring patterns
|
|
190
|
+
*/
|
|
191
|
+
extractRefactoringPatterns(modification) {
|
|
192
|
+
const patterns = [];
|
|
193
|
+
|
|
194
|
+
patterns.push({
|
|
195
|
+
type: 'refactoring',
|
|
196
|
+
refactoringType: modification.refactoringType,
|
|
197
|
+
triggers: modification.triggers || [],
|
|
198
|
+
steps: modification.steps || [],
|
|
199
|
+
validation: modification.validation || {},
|
|
200
|
+
benefits: modification.measuredBenefits || {}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
return patterns;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Extract dependency patterns
|
|
208
|
+
*/
|
|
209
|
+
extractDependencyPatterns(dependencyChanges) {
|
|
210
|
+
const patterns = [];
|
|
211
|
+
|
|
212
|
+
for (const change of dependencyChanges) {
|
|
213
|
+
if (change.type === 'consolidation') {
|
|
214
|
+
patterns.push({
|
|
215
|
+
type: 'dependency_consolidation',
|
|
216
|
+
from: change.originalDependencies,
|
|
217
|
+
to: change.consolidatedDependency,
|
|
218
|
+
reduction: change.dependencyReduction
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (change.type === 'upgrade') {
|
|
223
|
+
patterns.push({
|
|
224
|
+
type: 'dependency_upgrade',
|
|
225
|
+
dependency: change.dependency,
|
|
226
|
+
fromVersion: change.fromVersion,
|
|
227
|
+
toVersion: change.toVersion,
|
|
228
|
+
migrationSteps: change.migrationSteps
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return patterns;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Extract performance patterns
|
|
238
|
+
*/
|
|
239
|
+
extractPerformancePatterns(performanceImprovements) {
|
|
240
|
+
const patterns = [];
|
|
241
|
+
|
|
242
|
+
for (const improvement of performanceImprovements) {
|
|
243
|
+
patterns.push({
|
|
244
|
+
type: 'performance',
|
|
245
|
+
optimizationType: improvement.type,
|
|
246
|
+
technique: improvement.technique,
|
|
247
|
+
metrics: {
|
|
248
|
+
before: improvement.metricsBefore,
|
|
249
|
+
after: improvement.metricsAfter,
|
|
250
|
+
improvement: improvement.percentageImprovement
|
|
251
|
+
},
|
|
252
|
+
applicableContexts: improvement.contexts || []
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return patterns;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Learn from modification
|
|
261
|
+
*/
|
|
262
|
+
async learnFromModification(record) {
|
|
263
|
+
for (const pattern of record.patterns) {
|
|
264
|
+
const patternKey = this.generatePatternKey(pattern);
|
|
265
|
+
|
|
266
|
+
// Check if similar pattern exists
|
|
267
|
+
const similarPattern = await this.findSimilarPattern(pattern);
|
|
268
|
+
|
|
269
|
+
if (similarPattern) {
|
|
270
|
+
// Update existing pattern
|
|
271
|
+
await this.updatePattern(similarPattern, pattern, record);
|
|
272
|
+
} else {
|
|
273
|
+
// Create new pattern entry
|
|
274
|
+
await this.createPattern(patternKey, pattern, record);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Analyze cross-pattern relationships
|
|
279
|
+
await this.analyzePatternRelationships(record.patterns);
|
|
280
|
+
|
|
281
|
+
// Update pattern rankings
|
|
282
|
+
await this.updatePatternRankings();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Find similar pattern
|
|
287
|
+
*/
|
|
288
|
+
async findSimilarPattern(pattern) {
|
|
289
|
+
for (const [key, existingPattern] of this.patterns) {
|
|
290
|
+
const similarity = await this.calculatePatternSimilarity(pattern, existingPattern);
|
|
291
|
+
|
|
292
|
+
if (similarity >= this.similarityThreshold) {
|
|
293
|
+
return { key, pattern: existingPattern, similarity };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Calculate pattern similarity
|
|
302
|
+
*/
|
|
303
|
+
async calculatePatternSimilarity(pattern1, pattern2) {
|
|
304
|
+
// Type must match
|
|
305
|
+
if (pattern1.type !== pattern2.type) return 0;
|
|
306
|
+
|
|
307
|
+
let similarity = 0;
|
|
308
|
+
let factors = 0;
|
|
309
|
+
|
|
310
|
+
// Type-specific similarity calculation
|
|
311
|
+
switch (pattern1.type) {
|
|
312
|
+
case 'code_transformation':
|
|
313
|
+
similarity += this.calculateCodeSimilarity(pattern1, pattern2) * 0.7;
|
|
314
|
+
similarity += this.calculateContextSimilarity(pattern1.context, pattern2.context) * 0.3;
|
|
315
|
+
factors = 1;
|
|
316
|
+
break;
|
|
317
|
+
|
|
318
|
+
case 'structural':
|
|
319
|
+
similarity += this.calculateStructuralSimilarity(pattern1.pattern, pattern2.pattern) * 0.6;
|
|
320
|
+
similarity += this.calculateBenefitSimilarity(pattern1.benefits, pattern2.benefits) * 0.4;
|
|
321
|
+
factors = 1;
|
|
322
|
+
break;
|
|
323
|
+
|
|
324
|
+
case 'refactoring':
|
|
325
|
+
similarity += this.calculateRefactoringSimilarity(pattern1, pattern2);
|
|
326
|
+
factors = 1;
|
|
327
|
+
break;
|
|
328
|
+
|
|
329
|
+
case 'performance':
|
|
330
|
+
similarity += this.calculatePerformanceSimilarity(pattern1, pattern2);
|
|
331
|
+
factors = 1;
|
|
332
|
+
break;
|
|
333
|
+
|
|
334
|
+
default:
|
|
335
|
+
// Generic similarity based on pattern structure
|
|
336
|
+
similarity = this.calculateGenericSimilarity(pattern1, pattern2);
|
|
337
|
+
factors = 1;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return factors > 0 ? similarity / factors : 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Calculate code similarity
|
|
345
|
+
*/
|
|
346
|
+
calculateCodeSimilarity(pattern1, pattern2) {
|
|
347
|
+
const from1 = this.tokenizeCode(pattern1.from);
|
|
348
|
+
const from2 = this.tokenizeCode(pattern2.from);
|
|
349
|
+
const to1 = this.tokenizeCode(pattern1.to);
|
|
350
|
+
const to2 = this.tokenizeCode(pattern2.to);
|
|
351
|
+
|
|
352
|
+
const fromSimilarity = this.calculateTokenSimilarity(from1, from2);
|
|
353
|
+
const toSimilarity = this.calculateTokenSimilarity(to1, to2);
|
|
354
|
+
|
|
355
|
+
return (fromSimilarity + toSimilarity) / 2;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Tokenize code for comparison
|
|
360
|
+
*/
|
|
361
|
+
tokenizeCode(code) {
|
|
362
|
+
if (!code) return [];
|
|
363
|
+
|
|
364
|
+
// Simple tokenization - can be enhanced with proper AST parsing
|
|
365
|
+
return code
|
|
366
|
+
.replace(/\s+/g, ' ')
|
|
367
|
+
.replace(/[{}();,]/g, ' $& ')
|
|
368
|
+
.split(/\s+/)
|
|
369
|
+
.filter(token => token.length > 0);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Calculate token similarity
|
|
374
|
+
*/
|
|
375
|
+
calculateTokenSimilarity(tokens1, tokens2) {
|
|
376
|
+
const set1 = new Set(tokens1);
|
|
377
|
+
const set2 = new Set(tokens2);
|
|
378
|
+
|
|
379
|
+
const intersection = new Set([...set1].filter(x => set2.has(x)));
|
|
380
|
+
const union = new Set([...set1, ...set2]);
|
|
381
|
+
|
|
382
|
+
return union.size > 0 ? intersection.size / union.size : 0;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Update existing pattern
|
|
387
|
+
*/
|
|
388
|
+
async updatePattern(similarPattern, newPattern, record) {
|
|
389
|
+
const existingPattern = similarPattern.pattern;
|
|
390
|
+
|
|
391
|
+
// Update occurrence count
|
|
392
|
+
existingPattern.occurrences = (existingPattern.occurrences || 0) + 1;
|
|
393
|
+
|
|
394
|
+
// Update success rate
|
|
395
|
+
existingPattern.successCount = (existingPattern.successCount || 0) + 1;
|
|
396
|
+
existingPattern.successRate = existingPattern.successCount / existingPattern.occurrences;
|
|
397
|
+
|
|
398
|
+
// Merge benefits/improvements
|
|
399
|
+
if (newPattern.benefits) {
|
|
400
|
+
existingPattern.aggregatedBenefits = this.aggregateBenefits(
|
|
401
|
+
existingPattern.aggregatedBenefits || {},
|
|
402
|
+
newPattern.benefits
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Add to usage history
|
|
407
|
+
if (!existingPattern.usageHistory) {
|
|
408
|
+
existingPattern.usageHistory = [];
|
|
409
|
+
}
|
|
410
|
+
existingPattern.usageHistory.push({
|
|
411
|
+
recordId: record.id,
|
|
412
|
+
timestamp: record.timestamp,
|
|
413
|
+
author: record.metadata.author,
|
|
414
|
+
outcomes: record.outcomes
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// Update confidence score
|
|
418
|
+
existingPattern.confidence = this.calculatePatternConfidence(existingPattern);
|
|
419
|
+
|
|
420
|
+
// Check if pattern should be promoted
|
|
421
|
+
if (existingPattern.occurrences >= this.learningThreshold && existingPattern.confidence > 0.8) {
|
|
422
|
+
existingPattern.status = 'learned';
|
|
423
|
+
existingPattern.learnedAt = new Date().toISOString();
|
|
424
|
+
|
|
425
|
+
console.log(chalk.green(`✅ Pattern promoted to learned: ${similarPattern.key}`));
|
|
426
|
+
this.emit('pattern_learned', existingPattern);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Create new pattern
|
|
432
|
+
*/
|
|
433
|
+
async createPattern(key, pattern, record) {
|
|
434
|
+
const newPattern = {
|
|
435
|
+
...pattern,
|
|
436
|
+
key: key,
|
|
437
|
+
occurrences: 1,
|
|
438
|
+
successCount: 1,
|
|
439
|
+
successRate: 1.0,
|
|
440
|
+
firstSeen: record.timestamp,
|
|
441
|
+
lastSeen: record.timestamp,
|
|
442
|
+
status: 'candidate',
|
|
443
|
+
confidence: 0.3, // Initial low confidence
|
|
444
|
+
usageHistory: [{
|
|
445
|
+
recordId: record.id,
|
|
446
|
+
timestamp: record.timestamp,
|
|
447
|
+
author: record.metadata.author,
|
|
448
|
+
outcomes: record.outcomes
|
|
449
|
+
}]
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
this.patterns.set(key, newPattern);
|
|
453
|
+
|
|
454
|
+
console.log(chalk.gray(`New pattern candidate created: ${key}`));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Calculate pattern confidence
|
|
459
|
+
*/
|
|
460
|
+
calculatePatternConfidence(pattern) {
|
|
461
|
+
let confidence = 0;
|
|
462
|
+
|
|
463
|
+
// Occurrence factor (up to 0.3)
|
|
464
|
+
const occurrenceFactor = Math.min(pattern.occurrences / 10, 0.3);
|
|
465
|
+
confidence += occurrenceFactor;
|
|
466
|
+
|
|
467
|
+
// Success rate factor (up to 0.4)
|
|
468
|
+
confidence += pattern.successRate * 0.4;
|
|
469
|
+
|
|
470
|
+
// Consistency factor (up to 0.2)
|
|
471
|
+
const consistencyFactor = this.calculateConsistencyFactor(pattern.usageHistory);
|
|
472
|
+
confidence += consistencyFactor * 0.2;
|
|
473
|
+
|
|
474
|
+
// Recency factor (up to 0.1)
|
|
475
|
+
const recencyFactor = this.calculateRecencyFactor(pattern.lastSeen);
|
|
476
|
+
confidence += recencyFactor * 0.1;
|
|
477
|
+
|
|
478
|
+
return Math.min(confidence, 1.0);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Get pattern suggestions for modification
|
|
483
|
+
*/
|
|
484
|
+
async getPatternSuggestions(_context) {
|
|
485
|
+
const suggestions = [];
|
|
486
|
+
|
|
487
|
+
// Filter applicable patterns
|
|
488
|
+
const applicablePatterns = Array.from(this.patterns.values()).filter(pattern => {
|
|
489
|
+
return pattern.status === 'learned' &&
|
|
490
|
+
this.isPatternApplicable(pattern, context) &&
|
|
491
|
+
pattern.confidence > 0.7;
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// Sort by relevance and confidence
|
|
495
|
+
applicablePatterns.sort((a, b) => {
|
|
496
|
+
const relevanceA = this.calculateRelevance(a, context);
|
|
497
|
+
const relevanceB = this.calculateRelevance(b, context);
|
|
498
|
+
return (relevanceB * b.confidence) - (relevanceA * a.confidence);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Create suggestions
|
|
502
|
+
for (const pattern of applicablePatterns.slice(0, 5)) {
|
|
503
|
+
suggestions.push({
|
|
504
|
+
pattern: pattern,
|
|
505
|
+
relevance: this.calculateRelevance(pattern, context),
|
|
506
|
+
confidence: pattern.confidence,
|
|
507
|
+
expectedBenefits: pattern.aggregatedBenefits || {},
|
|
508
|
+
applicationGuide: await this.generateApplicationGuide(pattern, context),
|
|
509
|
+
examples: this.getPatternExamples(pattern)
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return suggestions;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Check if pattern is applicable
|
|
518
|
+
*/
|
|
519
|
+
isPatternApplicable(pattern, context) {
|
|
520
|
+
// Check component type compatibility
|
|
521
|
+
if (pattern.componentType && context.componentType) {
|
|
522
|
+
if (pattern.componentType !== context.componentType && pattern.componentType !== 'any') {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Check context requirements
|
|
528
|
+
if (pattern.requiredContext) {
|
|
529
|
+
for (const requirement of pattern.requiredContext) {
|
|
530
|
+
if (!this.meetsContextRequirement(_context, requirement)) {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Check applicability conditions
|
|
537
|
+
if (pattern.applicableContexts) {
|
|
538
|
+
return pattern.applicableContexts.some(ctx =>
|
|
539
|
+
this.matchesContext(_context, ctx)
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Generate application guide
|
|
548
|
+
*/
|
|
549
|
+
async generateApplicationGuide(pattern, context) {
|
|
550
|
+
const guide = {
|
|
551
|
+
steps: [],
|
|
552
|
+
preconditions: [],
|
|
553
|
+
expectedOutcome: {},
|
|
554
|
+
risks: [],
|
|
555
|
+
alternatives: []
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// Generate steps based on pattern type
|
|
559
|
+
switch (pattern.type) {
|
|
560
|
+
case 'code_transformation':
|
|
561
|
+
guide.steps = this.generateCodeTransformationSteps(pattern, context);
|
|
562
|
+
break;
|
|
563
|
+
case 'refactoring':
|
|
564
|
+
guide.steps = pattern.steps || [];
|
|
565
|
+
guide.preconditions = pattern.triggers || [];
|
|
566
|
+
break;
|
|
567
|
+
case 'performance':
|
|
568
|
+
guide.steps = this.generatePerformanceOptimizationSteps(pattern, context);
|
|
569
|
+
guide.expectedOutcome = pattern.metrics;
|
|
570
|
+
break;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Add general guidance
|
|
574
|
+
guide.confidence = `${Math.round(pattern.confidence * 100)}%`;
|
|
575
|
+
guide.successRate = `${Math.round(pattern.successRate * 100)}%`;
|
|
576
|
+
guide.usageCount = pattern.occurrences;
|
|
577
|
+
|
|
578
|
+
return guide;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Analyze pattern relationships
|
|
583
|
+
*/
|
|
584
|
+
async analyzePatternRelationships(patterns) {
|
|
585
|
+
// Find patterns that commonly occur together
|
|
586
|
+
const coOccurrences = new Map();
|
|
587
|
+
|
|
588
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
589
|
+
for (let j = i + 1; j < patterns.length; j++) {
|
|
590
|
+
const key = this.generateRelationshipKey(patterns[i], patterns[j]);
|
|
591
|
+
const existing = coOccurrences.get(key) || { count: 0, patterns: [] };
|
|
592
|
+
existing.count++;
|
|
593
|
+
existing.patterns = [patterns[i], patterns[j]];
|
|
594
|
+
coOccurrences.set(key, existing);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Store significant relationships
|
|
599
|
+
for (const [key, relationship] of coOccurrences) {
|
|
600
|
+
if (relationship.count >= 2) {
|
|
601
|
+
await this.storePatternRelationship(key, relationship);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Get pattern analytics
|
|
608
|
+
*/
|
|
609
|
+
async getPatternAnalytics() {
|
|
610
|
+
const analytics = {
|
|
611
|
+
totalPatterns: this.patterns.size,
|
|
612
|
+
learnedPatterns: 0,
|
|
613
|
+
candidatePatterns: 0,
|
|
614
|
+
patternsByType: {},
|
|
615
|
+
topPatterns: [],
|
|
616
|
+
recentTrends: [],
|
|
617
|
+
effectivenessMetrics: {}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
// Count patterns by status and type
|
|
621
|
+
for (const pattern of this.patterns.values()) {
|
|
622
|
+
if (pattern.status === 'learned') {
|
|
623
|
+
analytics.learnedPatterns++;
|
|
624
|
+
} else {
|
|
625
|
+
analytics.candidatePatterns++;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
analytics.patternsByType[pattern.type] =
|
|
629
|
+
(analytics.patternsByType[pattern.type] || 0) + 1;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// Get top patterns by usage
|
|
633
|
+
const sortedPatterns = Array.from(this.patterns.values())
|
|
634
|
+
.sort((a, b) => b.occurrences - a.occurrences);
|
|
635
|
+
|
|
636
|
+
analytics.topPatterns = sortedPatterns.slice(0, 10).map(p => ({
|
|
637
|
+
key: p.key,
|
|
638
|
+
type: p.type,
|
|
639
|
+
occurrences: p.occurrences,
|
|
640
|
+
successRate: p.successRate,
|
|
641
|
+
confidence: p.confidence
|
|
642
|
+
}));
|
|
643
|
+
|
|
644
|
+
// Calculate effectiveness metrics
|
|
645
|
+
analytics.effectivenessMetrics = await this.calculateEffectivenessMetrics();
|
|
646
|
+
|
|
647
|
+
// Get recent trends
|
|
648
|
+
analytics.recentTrends = await this.analyzeRecentTrends();
|
|
649
|
+
|
|
650
|
+
return analytics;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Calculate effectiveness metrics
|
|
655
|
+
*/
|
|
656
|
+
async calculateEffectivenessMetrics() {
|
|
657
|
+
const metrics = {
|
|
658
|
+
averageSuccessRate: 0,
|
|
659
|
+
averageConfidence: 0,
|
|
660
|
+
patternCoverage: 0,
|
|
661
|
+
learningRate: 0
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
const learnedPatterns = Array.from(this.patterns.values())
|
|
665
|
+
.filter(p => p.status === 'learned');
|
|
666
|
+
|
|
667
|
+
if (learnedPatterns.length > 0) {
|
|
668
|
+
metrics.averageSuccessRate = learnedPatterns.reduce((sum, p) =>
|
|
669
|
+
sum + p.successRate, 0) / learnedPatterns.length;
|
|
670
|
+
|
|
671
|
+
metrics.averageConfidence = learnedPatterns.reduce((sum, p) =>
|
|
672
|
+
sum + p.confidence, 0) / learnedPatterns.length;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Calculate pattern coverage
|
|
676
|
+
const modificationTypes = new Set(this.modificationHistory.map(m => m.modificationType));
|
|
677
|
+
const coveredTypes = new Set(learnedPatterns.map(p => p.type));
|
|
678
|
+
metrics.patternCoverage = modificationTypes.size > 0 ?
|
|
679
|
+
coveredTypes.size / modificationTypes.size : 0;
|
|
680
|
+
|
|
681
|
+
// Calculate learning rate
|
|
682
|
+
const recentHistory = this.modificationHistory.slice(-20);
|
|
683
|
+
const recentLearned = recentHistory.filter(m =>
|
|
684
|
+
m.patterns.some(p => this.patterns.get(this.generatePatternKey(p))?.status === 'learned')
|
|
685
|
+
);
|
|
686
|
+
metrics.learningRate = recentHistory.length > 0 ?
|
|
687
|
+
recentLearned.length / recentHistory.length : 0;
|
|
688
|
+
|
|
689
|
+
return metrics;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Helper methods
|
|
694
|
+
*/
|
|
695
|
+
|
|
696
|
+
normalizeCode(code) {
|
|
697
|
+
if (!code) return '';
|
|
698
|
+
return code.trim().replace(/\s+/g, ' ');
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
generatePatternKey(pattern) {
|
|
702
|
+
return `${pattern.type}:${pattern.subtype || 'default'}:${
|
|
703
|
+
crypto.createHash('md5').update(JSON.stringify(pattern)).digest('hex').substr(0, 8)
|
|
704
|
+
}`;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
aggregateBenefits(existing, newBenefits) {
|
|
708
|
+
const aggregated = { ...existing };
|
|
709
|
+
|
|
710
|
+
for (const [key, value] of Object.entries(newBenefits)) {
|
|
711
|
+
if (typeof value === 'number') {
|
|
712
|
+
aggregated[key] = (aggregated[key] || 0) + value;
|
|
713
|
+
aggregated[`${key}_avg`] = aggregated[key] / ((aggregated[`${key}_count`] || 0) + 1);
|
|
714
|
+
aggregated[`${key}_count`] = (aggregated[`${key}_count`] || 0) + 1;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return aggregated;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
calculateConsistencyFactor(usageHistory) {
|
|
722
|
+
if (usageHistory.length < 2) return 1.0;
|
|
723
|
+
|
|
724
|
+
// Check time intervals between uses
|
|
725
|
+
const intervals = [];
|
|
726
|
+
for (let i = 1; i < usageHistory.length; i++) {
|
|
727
|
+
const interval = new Date(usageHistory[i].timestamp) - new Date(usageHistory[i-1].timestamp);
|
|
728
|
+
intervals.push(interval);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Calculate variance
|
|
732
|
+
const avgInterval = intervals.reduce((sum, i) => sum + i, 0) / intervals.length;
|
|
733
|
+
const variance = intervals.reduce((sum, i) => sum + Math.pow(i - avgInterval, 2), 0) / intervals.length;
|
|
734
|
+
const stdDev = Math.sqrt(variance);
|
|
735
|
+
|
|
736
|
+
// Lower variance = higher consistency
|
|
737
|
+
return 1 / (1 + stdDev / avgInterval);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
calculateRecencyFactor(lastSeen) {
|
|
741
|
+
const daysSinceLastSeen = (Date.now() - new Date(lastSeen).getTime()) / (1000 * 60 * 60 * 24);
|
|
742
|
+
return Math.max(0, 1 - daysSinceLastSeen / 30); // Decay over 30 days
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
calculateRelevance(pattern, context) {
|
|
746
|
+
let relevance = 0;
|
|
747
|
+
|
|
748
|
+
// Type match
|
|
749
|
+
if (pattern.type === context.modificationType) {
|
|
750
|
+
relevance += 0.3;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Component type match
|
|
754
|
+
if (pattern.componentType === context.componentType) {
|
|
755
|
+
relevance += 0.2;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// Context similarity
|
|
759
|
+
if (pattern.context && context.currentContext) {
|
|
760
|
+
relevance += this.calculateContextSimilarity(pattern.context, context.currentContext) * 0.3;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Goal alignment
|
|
764
|
+
if (pattern.benefits && context.goals) {
|
|
765
|
+
relevance += this.calculateGoalAlignment(pattern.benefits, context.goals) * 0.2;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return relevance;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
getPatternExamples(pattern) {
|
|
772
|
+
return pattern.usageHistory
|
|
773
|
+
.slice(-3)
|
|
774
|
+
.map(usage => ({
|
|
775
|
+
recordId: usage.recordId,
|
|
776
|
+
timestamp: usage.timestamp,
|
|
777
|
+
author: usage.author,
|
|
778
|
+
outcomes: usage.outcomes
|
|
779
|
+
}));
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Save and load methods
|
|
784
|
+
*/
|
|
785
|
+
|
|
786
|
+
async saveHistory() {
|
|
787
|
+
await fs.writeFile(
|
|
788
|
+
this.historyFile,
|
|
789
|
+
JSON.stringify(this.modificationHistory, null, 2)
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async loadHistory() {
|
|
794
|
+
try {
|
|
795
|
+
const content = await fs.readFile(this.historyFile, 'utf-8');
|
|
796
|
+
this.modificationHistory = JSON.parse(content);
|
|
797
|
+
} catch (_error) {
|
|
798
|
+
// No history file yet
|
|
799
|
+
this.modificationHistory = [];
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
async savePatterns() {
|
|
804
|
+
const patternsArray = Array.from(this.patterns.entries()).map(([key, pattern]) => ({
|
|
805
|
+
key,
|
|
806
|
+
...pattern
|
|
807
|
+
}));
|
|
808
|
+
|
|
809
|
+
await fs.writeFile(
|
|
810
|
+
this.patternsFile,
|
|
811
|
+
JSON.stringify(patternsArray, null, 2)
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
async loadPatterns() {
|
|
816
|
+
try {
|
|
817
|
+
const content = await fs.readFile(this.patternsFile, 'utf-8');
|
|
818
|
+
const patternsArray = JSON.parse(content);
|
|
819
|
+
|
|
820
|
+
this.patterns.clear();
|
|
821
|
+
for (const pattern of patternsArray) {
|
|
822
|
+
this.patterns.set(pattern.key, pattern);
|
|
823
|
+
}
|
|
824
|
+
} catch (_error) {
|
|
825
|
+
// No patterns file yet
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* Check learning threshold
|
|
831
|
+
*/
|
|
832
|
+
async checkLearningThreshold(patterns) {
|
|
833
|
+
let learnedCount = 0;
|
|
834
|
+
|
|
835
|
+
for (const pattern of patterns) {
|
|
836
|
+
const key = this.generatePatternKey(pattern);
|
|
837
|
+
const existing = this.patterns.get(key);
|
|
838
|
+
|
|
839
|
+
if (existing && existing.occurrences >= this.learningThreshold) {
|
|
840
|
+
learnedCount++;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
return learnedCount > 0;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
calculateContextSimilarity(context1, context2) {
|
|
848
|
+
// Simple context similarity - can be enhanced
|
|
849
|
+
if (!context1 || !context2) return 0;
|
|
850
|
+
|
|
851
|
+
const keys1 = Object.keys(context1);
|
|
852
|
+
const keys2 = Object.keys(context2);
|
|
853
|
+
const commonKeys = keys1.filter(k => keys2.includes(k));
|
|
854
|
+
|
|
855
|
+
if (commonKeys.length === 0) return 0;
|
|
856
|
+
|
|
857
|
+
let similarity = commonKeys.length / Math.max(keys1.length, keys2.length);
|
|
858
|
+
|
|
859
|
+
// Check value similarity for common keys
|
|
860
|
+
for (const key of commonKeys) {
|
|
861
|
+
if (context1[key] === context2[key]) {
|
|
862
|
+
similarity += 0.1;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return Math.min(similarity, 1.0);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
calculateStructuralSimilarity(struct1, struct2) {
|
|
870
|
+
// Compare structural patterns
|
|
871
|
+
if (!struct1 || !struct2) return 0;
|
|
872
|
+
|
|
873
|
+
const before1 = JSON.stringify(struct1.before);
|
|
874
|
+
const before2 = JSON.stringify(struct2.before);
|
|
875
|
+
const after1 = JSON.stringify(struct1.after);
|
|
876
|
+
const after2 = JSON.stringify(struct2.after);
|
|
877
|
+
|
|
878
|
+
const beforeSim = before1 === before2 ? 1 : 0.5;
|
|
879
|
+
const afterSim = after1 === after2 ? 1 : 0.5;
|
|
880
|
+
|
|
881
|
+
return (beforeSim + afterSim) / 2;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
calculateBenefitSimilarity(benefits1, benefits2) {
|
|
885
|
+
if (!benefits1 || !benefits2) return 0;
|
|
886
|
+
|
|
887
|
+
const keys1 = Object.keys(benefits1);
|
|
888
|
+
const keys2 = Object.keys(benefits2);
|
|
889
|
+
const allKeys = new Set([...keys1, ...keys2]);
|
|
890
|
+
|
|
891
|
+
let similarity = 0;
|
|
892
|
+
for (const key of allKeys) {
|
|
893
|
+
if (benefits1[key] && benefits2[key]) {
|
|
894
|
+
// Both have the benefit
|
|
895
|
+
similarity += 1;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
return allKeys.size > 0 ? similarity / allKeys.size : 0;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
calculateRefactoringSimilarity(refactor1, refactor2) {
|
|
903
|
+
if (refactor1.refactoringType !== refactor2.refactoringType) return 0;
|
|
904
|
+
|
|
905
|
+
let similarity = 0.5; // Base similarity for same type
|
|
906
|
+
|
|
907
|
+
// Compare triggers
|
|
908
|
+
if (refactor1.triggers && refactor2.triggers) {
|
|
909
|
+
const commonTriggers = refactor1.triggers.filter(t =>
|
|
910
|
+
refactor2.triggers.includes(t)
|
|
911
|
+
);
|
|
912
|
+
similarity += commonTriggers.length / Math.max(refactor1.triggers.length, refactor2.triggers.length) * 0.3;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// Compare steps
|
|
916
|
+
if (refactor1.steps && refactor2.steps) {
|
|
917
|
+
const stepSimilarity = Math.min(refactor1.steps.length, refactor2.steps.length) /
|
|
918
|
+
Math.max(refactor1.steps.length, refactor2.steps.length);
|
|
919
|
+
similarity += stepSimilarity * 0.2;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
return similarity;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
calculatePerformanceSimilarity(perf1, perf2) {
|
|
926
|
+
if (perf1.optimizationType !== perf2.optimizationType) return 0;
|
|
927
|
+
|
|
928
|
+
let similarity = 0.4; // Base similarity for same type
|
|
929
|
+
|
|
930
|
+
if (perf1.technique === perf2.technique) {
|
|
931
|
+
similarity += 0.3;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
// Compare applicable contexts
|
|
935
|
+
if (perf1.applicableContexts && perf2.applicableContexts) {
|
|
936
|
+
const commonContexts = perf1.applicableContexts.filter(c =>
|
|
937
|
+
perf2.applicableContexts.includes(c)
|
|
938
|
+
);
|
|
939
|
+
similarity += commonContexts.length / Math.max(perf1.applicableContexts.length, perf2.applicableContexts.length) * 0.3;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
return similarity;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
calculateGenericSimilarity(pattern1, pattern2) {
|
|
946
|
+
// Generic JSON similarity
|
|
947
|
+
const json1 = JSON.stringify(pattern1);
|
|
948
|
+
const json2 = JSON.stringify(pattern2);
|
|
949
|
+
|
|
950
|
+
if (json1 === json2) return 1.0;
|
|
951
|
+
|
|
952
|
+
// Calculate Levenshtein distance ratio
|
|
953
|
+
const distance = this.levenshteinDistance(json1, json2);
|
|
954
|
+
const maxLength = Math.max(json1.length, json2.length);
|
|
955
|
+
|
|
956
|
+
return 1 - (distance / maxLength);
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
levenshteinDistance(str1, str2) {
|
|
960
|
+
const matrix = [];
|
|
961
|
+
|
|
962
|
+
for (let i = 0; i <= str2.length; i++) {
|
|
963
|
+
matrix[i] = [i];
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
for (let j = 0; j <= str1.length; j++) {
|
|
967
|
+
matrix[0][j] = j;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
for (let i = 1; i <= str2.length; i++) {
|
|
971
|
+
for (let j = 1; j <= str1.length; j++) {
|
|
972
|
+
if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
|
|
973
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
974
|
+
} else {
|
|
975
|
+
matrix[i][j] = Math.min(
|
|
976
|
+
matrix[i - 1][j - 1] + 1,
|
|
977
|
+
matrix[i][j - 1] + 1,
|
|
978
|
+
matrix[i - 1][j] + 1
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
return matrix[str2.length][str1.length];
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
meetsContextRequirement(_context, requirement) {
|
|
988
|
+
// Check if context meets specific requirement
|
|
989
|
+
if (requirement.type === 'has_property') {
|
|
990
|
+
return context[requirement.property] !== undefined;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (requirement.type === 'property_value') {
|
|
994
|
+
return context[requirement.property] === requirement.value;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (requirement.type === 'property_range') {
|
|
998
|
+
const value = context[requirement.property];
|
|
999
|
+
return value >= requirement.min && value <= requirement.max;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
return true;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
matchesContext(_context, patternContext) {
|
|
1006
|
+
// Check if contexts match
|
|
1007
|
+
for (const [key, value] of Object.entries(patternContext)) {
|
|
1008
|
+
if (context[key] !== value) {
|
|
1009
|
+
return false;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
generateCodeTransformationSteps(pattern, context) {
|
|
1016
|
+
const steps = [];
|
|
1017
|
+
|
|
1018
|
+
steps.push({
|
|
1019
|
+
step: 1,
|
|
1020
|
+
action: 'Identify target code pattern',
|
|
1021
|
+
description: `Look for code matching: ${pattern.from}`,
|
|
1022
|
+
validation: 'Ensure code structure matches the pattern'
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
steps.push({
|
|
1026
|
+
step: 2,
|
|
1027
|
+
action: 'Apply transformation',
|
|
1028
|
+
description: `Transform to: ${pattern.to}`,
|
|
1029
|
+
validation: 'Verify transformation preserves functionality'
|
|
1030
|
+
});
|
|
1031
|
+
|
|
1032
|
+
if (pattern.context) {
|
|
1033
|
+
steps.push({
|
|
1034
|
+
step: 3,
|
|
1035
|
+
action: 'Validate context',
|
|
1036
|
+
description: 'Ensure transformation is appropriate for context',
|
|
1037
|
+
validation: pattern.context
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
steps.push({
|
|
1042
|
+
step: 4,
|
|
1043
|
+
action: 'Test changes',
|
|
1044
|
+
description: 'Run tests to ensure no regression',
|
|
1045
|
+
validation: 'All tests pass'
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
return steps;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
generatePerformanceOptimizationSteps(pattern, context) {
|
|
1052
|
+
const steps = [];
|
|
1053
|
+
|
|
1054
|
+
steps.push({
|
|
1055
|
+
step: 1,
|
|
1056
|
+
action: 'Measure baseline performance',
|
|
1057
|
+
description: 'Capture current performance metrics',
|
|
1058
|
+
validation: 'Baseline metrics recorded'
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
steps.push({
|
|
1062
|
+
step: 2,
|
|
1063
|
+
action: `Apply ${pattern.technique} optimization`,
|
|
1064
|
+
description: pattern.description || 'Apply performance optimization technique',
|
|
1065
|
+
validation: 'Optimization applied correctly'
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
steps.push({
|
|
1069
|
+
step: 3,
|
|
1070
|
+
action: 'Measure improved performance',
|
|
1071
|
+
description: 'Capture post-optimization metrics',
|
|
1072
|
+
validation: `Expected improvement: ${pattern.metrics.improvement}%`
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
steps.push({
|
|
1076
|
+
step: 4,
|
|
1077
|
+
action: 'Validate functionality',
|
|
1078
|
+
description: 'Ensure optimization didn\'t break functionality',
|
|
1079
|
+
validation: 'All tests pass'
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
return steps;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
generateRelationshipKey(pattern1, pattern2) {
|
|
1086
|
+
const types = [pattern1.type, pattern2.type].sort();
|
|
1087
|
+
return `rel:${types.join(':')}`;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
async storePatternRelationship(key, relationship) {
|
|
1091
|
+
// Store pattern relationships for future analysis
|
|
1092
|
+
const relationshipsFile = path.join(this.patternsDir, 'relationships.json');
|
|
1093
|
+
|
|
1094
|
+
let relationships = {};
|
|
1095
|
+
try {
|
|
1096
|
+
const content = await fs.readFile(relationshipsFile, 'utf-8');
|
|
1097
|
+
relationships = JSON.parse(content);
|
|
1098
|
+
} catch (_error) {
|
|
1099
|
+
// No relationships file yet
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
relationships[key] = relationship;
|
|
1103
|
+
|
|
1104
|
+
await fs.writeFile(relationshipsFile, JSON.stringify(relationships, null, 2));
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
async analyzeRecentTrends() {
|
|
1108
|
+
const recentModifications = this.modificationHistory.slice(-30);
|
|
1109
|
+
const trends = {
|
|
1110
|
+
emergingPatterns: [],
|
|
1111
|
+
decliningPatterns: [],
|
|
1112
|
+
stablePatterns: []
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
// Analyze pattern usage over time
|
|
1116
|
+
const patternUsage = new Map();
|
|
1117
|
+
|
|
1118
|
+
for (const mod of recentModifications) {
|
|
1119
|
+
for (const pattern of mod.patterns) {
|
|
1120
|
+
const key = this.generatePatternKey(pattern);
|
|
1121
|
+
const usage = patternUsage.get(key) || { count: 0, recent: 0 };
|
|
1122
|
+
usage.count++;
|
|
1123
|
+
|
|
1124
|
+
// Check if in last 10 modifications
|
|
1125
|
+
const modIndex = recentModifications.indexOf(mod);
|
|
1126
|
+
if (modIndex >= recentModifications.length - 10) {
|
|
1127
|
+
usage.recent++;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
patternUsage.set(key, usage);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// Classify patterns
|
|
1135
|
+
for (const [key, usage] of patternUsage) {
|
|
1136
|
+
const pattern = this.patterns.get(key);
|
|
1137
|
+
if (!pattern) continue;
|
|
1138
|
+
|
|
1139
|
+
const recentRatio = usage.recent / usage.count;
|
|
1140
|
+
|
|
1141
|
+
if (recentRatio > 0.6) {
|
|
1142
|
+
trends.emergingPatterns.push({
|
|
1143
|
+
key: key,
|
|
1144
|
+
type: pattern.type,
|
|
1145
|
+
trend: 'emerging',
|
|
1146
|
+
usage: usage
|
|
1147
|
+
});
|
|
1148
|
+
} else if (recentRatio < 0.2) {
|
|
1149
|
+
trends.decliningPatterns.push({
|
|
1150
|
+
key: key,
|
|
1151
|
+
type: pattern.type,
|
|
1152
|
+
trend: 'declining',
|
|
1153
|
+
usage: usage
|
|
1154
|
+
});
|
|
1155
|
+
} else {
|
|
1156
|
+
trends.stablePatterns.push({
|
|
1157
|
+
key: key,
|
|
1158
|
+
type: pattern.type,
|
|
1159
|
+
trend: 'stable',
|
|
1160
|
+
usage: usage
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
return trends;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
calculateGoalAlignment(benefits, goals) {
|
|
1169
|
+
if (!benefits || !goals) return 0;
|
|
1170
|
+
|
|
1171
|
+
let alignment = 0;
|
|
1172
|
+
let _matchedGoals = 0;
|
|
1173
|
+
|
|
1174
|
+
for (const goal of goals) {
|
|
1175
|
+
if (goal.type === 'performance' && benefits.performanceImprovement) {
|
|
1176
|
+
alignment += benefits.performanceImprovement > goal.target ? 1 : 0.5;
|
|
1177
|
+
matchedGoals++;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
if (goal.type === 'maintainability' && benefits.maintainability) {
|
|
1181
|
+
alignment += benefits.maintainability > goal.target ? 1 : 0.5;
|
|
1182
|
+
matchedGoals++;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
if (goal.type === 'testability' && benefits.testability) {
|
|
1186
|
+
alignment += benefits.testability > goal.target ? 1 : 0.5;
|
|
1187
|
+
matchedGoals++;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
return goals.length > 0 ? alignment / goals.length : 0;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
async updatePatternRankings() {
|
|
1195
|
+
// Update pattern rankings based on multiple factors
|
|
1196
|
+
for (const pattern of this.patterns.values()) {
|
|
1197
|
+
pattern.ranking = this.calculatePatternRanking(pattern);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// Save updated patterns
|
|
1201
|
+
await this.savePatterns();
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
calculatePatternRanking(pattern) {
|
|
1205
|
+
let ranking = 0;
|
|
1206
|
+
|
|
1207
|
+
// Success rate (40%)
|
|
1208
|
+
ranking += pattern.successRate * 40;
|
|
1209
|
+
|
|
1210
|
+
// Usage frequency (30%)
|
|
1211
|
+
const usageScore = Math.min(pattern.occurrences / 20, 1);
|
|
1212
|
+
ranking += usageScore * 30;
|
|
1213
|
+
|
|
1214
|
+
// Confidence (20%)
|
|
1215
|
+
ranking += pattern.confidence * 20;
|
|
1216
|
+
|
|
1217
|
+
// Recency (10%)
|
|
1218
|
+
const recencyScore = this.calculateRecencyFactor(pattern.lastSeen);
|
|
1219
|
+
ranking += recencyScore * 10;
|
|
1220
|
+
|
|
1221
|
+
return ranking;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
1225
|
module.exports = PatternLearner;
|