add-skill-kit 3.2.4 → 3.2.6
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/README.md +179 -119
- package/bin/lib/commands/help.js +0 -4
- package/bin/lib/commands/install.js +129 -9
- package/bin/lib/ui.js +1 -1
- package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
- package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
- package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
- package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
- package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
- package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
- package/lib/agent-cli/bin/{ag-smart.js → agent.js} +48 -15
- package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
- package/lib/agent-cli/dashboard/index.html +538 -0
- package/lib/agent-cli/lib/audit.js +2 -2
- package/lib/agent-cli/lib/auto-learn.js +8 -8
- package/lib/agent-cli/lib/eslint-fix.js +1 -1
- package/lib/agent-cli/lib/fix.js +5 -5
- package/lib/agent-cli/lib/hooks/install-hooks.js +4 -4
- package/lib/agent-cli/lib/hooks/lint-learn.js +4 -4
- package/lib/agent-cli/lib/learn.js +10 -10
- package/lib/agent-cli/lib/recall.js +1 -1
- package/lib/agent-cli/lib/settings.js +24 -0
- package/lib/agent-cli/lib/skill-learn.js +2 -2
- package/lib/agent-cli/lib/stats.js +3 -3
- package/lib/agent-cli/lib/ui/dashboard-ui.js +103 -4
- package/lib/agent-cli/lib/ui/index.js +36 -6
- package/lib/agent-cli/lib/watcher.js +2 -2
- package/lib/agent-cli/package.json +4 -4
- package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
- package/lib/agent-cli/scripts/dashboard_server.js +224 -0
- package/lib/agent-cli/scripts/error_sensor.js +565 -0
- package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
- package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
- package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
- package/lib/agent-cli/scripts/rule_sharing.js +374 -0
- package/lib/agent-cli/scripts/skill_injector.js +387 -0
- package/lib/agent-cli/scripts/success_sensor.js +500 -0
- package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
- package/lib/agent-cli/services/auto-learn-service.js +247 -0
- package/lib/agent-cli/src/MIGRATION.md +418 -0
- package/lib/agent-cli/src/README.md +367 -0
- package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
- package/lib/agent-cli/src/core/evolution/index.js +17 -0
- package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
- package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
- package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
- package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
- package/lib/agent-cli/src/core/index.js +15 -0
- package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
- package/lib/agent-cli/src/core/learning/index.js +12 -0
- package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
- package/lib/agent-cli/src/core/scanning/index.js +14 -0
- package/lib/agent-cli/src/data/index.js +13 -0
- package/lib/agent-cli/src/data/repositories/index.js +8 -0
- package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
- package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
- package/lib/agent-cli/src/data/storage/index.js +8 -0
- package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
- package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
- package/lib/agent-cli/src/infrastructure/index.js +13 -0
- package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
- package/lib/agent-cli/src/services/export-service.js +162 -0
- package/lib/agent-cli/src/services/index.js +13 -0
- package/lib/agent-cli/src/services/learning-service.js +99 -0
- package/lib/agent-cli/types/index.d.ts +343 -0
- package/lib/agent-cli/utils/benchmark.js +269 -0
- package/lib/agent-cli/utils/logger.js +303 -0
- package/lib/agent-cli/utils/ml_patterns.js +300 -0
- package/lib/agent-cli/utils/recovery.js +312 -0
- package/lib/agent-cli/utils/telemetry.js +290 -0
- package/lib/agentskillskit-cli/ag-smart.js +15 -15
- package/lib/agentskillskit-cli/package.json +3 -3
- package/package.json +12 -6
- package/lib/agent-cli/lib/auto_preview.py +0 -148
- package/lib/agent-cli/lib/checklist.py +0 -222
- package/lib/agent-cli/lib/session_manager.py +0 -120
- package/lib/agent-cli/lib/verify_all.py +0 -327
- /package/bin/{cli.js → kit.js} +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LearningService - Application Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates learning workflow:
|
|
5
|
+
* - Load raw data (mistakes, improvements)
|
|
6
|
+
* - Synthesize cognitive lessons
|
|
7
|
+
* - Manage lesson CRUD operations
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { LessonSynthesizer } from '../core/learning/lesson-synthesizer.js';
|
|
11
|
+
|
|
12
|
+
export class LearningService {
|
|
13
|
+
constructor(lessonRepository) {
|
|
14
|
+
this.lessonRepository = lessonRepository;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get all cognitive lessons
|
|
19
|
+
* @returns {Promise<Array>}
|
|
20
|
+
*/
|
|
21
|
+
async getCognitiveLessons() {
|
|
22
|
+
const mistakesDb = await this.lessonRepository.loadMistakes();
|
|
23
|
+
const improvementsDb = await this.lessonRepository.loadImprovements();
|
|
24
|
+
|
|
25
|
+
return LessonSynthesizer.synthesize(
|
|
26
|
+
mistakesDb.mistakes || [],
|
|
27
|
+
improvementsDb.improvements || []
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Add a lesson to legacy format
|
|
33
|
+
* @param {string} pattern - Regex pattern
|
|
34
|
+
* @param {string} message - Explanation message
|
|
35
|
+
* @param {string} severity - WARNING or ERROR
|
|
36
|
+
* @param {string} category - Category tag
|
|
37
|
+
* @returns {Promise<object>}
|
|
38
|
+
*/
|
|
39
|
+
async addLesson(pattern, message, severity = 'WARNING', category = 'general') {
|
|
40
|
+
// Validate Regex
|
|
41
|
+
try {
|
|
42
|
+
new RegExp(pattern);
|
|
43
|
+
} catch (e) {
|
|
44
|
+
throw new Error(`Invalid Regex pattern: ${e.message}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Validate severity
|
|
48
|
+
if (!['WARNING', 'ERROR'].includes(severity.toUpperCase())) {
|
|
49
|
+
throw new Error('Invalid severity. Must be WARNING or ERROR');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check for duplicates
|
|
53
|
+
const db = await this.lessonRepository.loadLegacyLessons();
|
|
54
|
+
const exists = db.lessons.some(l => l.pattern === pattern);
|
|
55
|
+
|
|
56
|
+
if (exists) {
|
|
57
|
+
throw new Error('Pattern already exists in knowledge base');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const id = `LEARN-${String(db.lessons.length + 1).padStart(3, '0')}`;
|
|
61
|
+
|
|
62
|
+
const lesson = {
|
|
63
|
+
id,
|
|
64
|
+
pattern,
|
|
65
|
+
message,
|
|
66
|
+
severity: severity.toUpperCase(),
|
|
67
|
+
category,
|
|
68
|
+
source: 'manual',
|
|
69
|
+
hitCount: 0,
|
|
70
|
+
lastHit: null,
|
|
71
|
+
autoEscalated: false,
|
|
72
|
+
addedAt: new Date().toISOString()
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return this.lessonRepository.addLegacyLesson(lesson);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Remove a lesson
|
|
80
|
+
* @param {string} lessonId
|
|
81
|
+
*/
|
|
82
|
+
async removeLesson(lessonId) {
|
|
83
|
+
return this.lessonRepository.removeLegacyLesson(lessonId);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* List all lessons
|
|
88
|
+
* @param {string} category - Optional category filter
|
|
89
|
+
*/
|
|
90
|
+
async listLessons(category = null) {
|
|
91
|
+
const db = await this.lessonRepository.loadLegacyLessons();
|
|
92
|
+
|
|
93
|
+
if (category) {
|
|
94
|
+
return db.lessons.filter(l => l.category === category);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return db.lessons;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Learn System TypeScript Definitions
|
|
3
|
+
*
|
|
4
|
+
* Provides type safety for consumers of Auto-Learn scripts.
|
|
5
|
+
* These types match the JSON structures used in .agent/knowledge/
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ==================== CORE TYPES ====================
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Severity levels for rules and lessons
|
|
12
|
+
*/
|
|
13
|
+
export type Severity = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Risk level for intent detection
|
|
17
|
+
*/
|
|
18
|
+
export type RiskLevel = 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Health status for trend analysis
|
|
22
|
+
*/
|
|
23
|
+
export type HealthStatus = 'IMPROVING' | 'STABLE' | 'NEUTRAL' | 'DECLINING';
|
|
24
|
+
|
|
25
|
+
// ==================== LESSONS ====================
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A learned lesson from errors or corrections
|
|
29
|
+
*/
|
|
30
|
+
export interface Lesson {
|
|
31
|
+
id: string;
|
|
32
|
+
pattern: string;
|
|
33
|
+
message: string;
|
|
34
|
+
severity: Severity;
|
|
35
|
+
hitCount?: number;
|
|
36
|
+
successCount?: number;
|
|
37
|
+
lastHit?: string;
|
|
38
|
+
promoted?: boolean;
|
|
39
|
+
promotedAt?: string;
|
|
40
|
+
demoted?: boolean;
|
|
41
|
+
demotedAt?: string;
|
|
42
|
+
previousSeverity?: Severity;
|
|
43
|
+
confidence?: number;
|
|
44
|
+
adjustedAt?: string;
|
|
45
|
+
importedAt?: string;
|
|
46
|
+
source?: 'auto' | 'manual' | 'imported';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Collection of lessons
|
|
51
|
+
*/
|
|
52
|
+
export interface LessonsData {
|
|
53
|
+
lessons: Lesson[];
|
|
54
|
+
lastUpdated?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ==================== ERRORS ====================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A detected error
|
|
61
|
+
*/
|
|
62
|
+
export interface DetectedError {
|
|
63
|
+
id: string;
|
|
64
|
+
type: string;
|
|
65
|
+
source: 'test' | 'build' | 'lint' | 'runtime' | 'code';
|
|
66
|
+
message: string;
|
|
67
|
+
file?: string;
|
|
68
|
+
line?: number;
|
|
69
|
+
severity: Severity;
|
|
70
|
+
timestamp: string;
|
|
71
|
+
context?: ErrorContext;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Error context information
|
|
76
|
+
*/
|
|
77
|
+
export interface ErrorContext {
|
|
78
|
+
project?: ProjectContext;
|
|
79
|
+
file?: FileContext;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Collection of errors
|
|
84
|
+
*/
|
|
85
|
+
export interface ErrorsData {
|
|
86
|
+
errors: DetectedError[];
|
|
87
|
+
lastUpdated?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ==================== SUCCESSES ====================
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A detected success pattern
|
|
94
|
+
*/
|
|
95
|
+
export interface SuccessPattern {
|
|
96
|
+
id: string;
|
|
97
|
+
pattern: string;
|
|
98
|
+
type: 'pattern' | 'practice' | 'test' | 'build' | 'lint';
|
|
99
|
+
file?: string;
|
|
100
|
+
detectedAt: string;
|
|
101
|
+
context?: SuccessContext;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Success context information
|
|
106
|
+
*/
|
|
107
|
+
export interface SuccessContext {
|
|
108
|
+
project?: ProjectContext;
|
|
109
|
+
file?: FileContext;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Collection of successes
|
|
114
|
+
*/
|
|
115
|
+
export interface SuccessesData {
|
|
116
|
+
successes: SuccessPattern[];
|
|
117
|
+
lastUpdated?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ==================== CONTEXT ====================
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Project context information
|
|
124
|
+
*/
|
|
125
|
+
export interface ProjectContext {
|
|
126
|
+
projectType: 'unknown' | 'nextjs' | 'react' | 'react-native' | 'backend' | 'nodejs';
|
|
127
|
+
framework?: string;
|
|
128
|
+
language: 'javascript' | 'typescript';
|
|
129
|
+
hasTypeScript?: boolean;
|
|
130
|
+
hasTesting?: boolean;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* File context information
|
|
135
|
+
*/
|
|
136
|
+
export interface FileContext {
|
|
137
|
+
fileType: string;
|
|
138
|
+
isTypeScript: boolean;
|
|
139
|
+
isTest: boolean;
|
|
140
|
+
isComponent: boolean;
|
|
141
|
+
directory: string;
|
|
142
|
+
fullPath: string;
|
|
143
|
+
area?: 'source' | 'library' | 'test' | 'config' | 'other';
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ==================== RULES ====================
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A matched rule from pre-execution check
|
|
150
|
+
*/
|
|
151
|
+
export interface RuleMatch {
|
|
152
|
+
id: string;
|
|
153
|
+
name?: string;
|
|
154
|
+
pattern?: string;
|
|
155
|
+
prevention: string;
|
|
156
|
+
severity: Severity;
|
|
157
|
+
source: 'builtin' | 'lesson' | 'auto-rule' | 'active-rule' | 'intent-detection';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* An auto-generated rule
|
|
162
|
+
*/
|
|
163
|
+
export interface AutoRule {
|
|
164
|
+
id: string;
|
|
165
|
+
pattern: string;
|
|
166
|
+
prevention: string;
|
|
167
|
+
severity: Severity;
|
|
168
|
+
status: 'proposed' | 'approved' | 'rejected';
|
|
169
|
+
hitCount?: number;
|
|
170
|
+
successRate?: number;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ==================== INTENT DETECTION ====================
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* A detected intent from user input
|
|
177
|
+
*/
|
|
178
|
+
export interface DetectedIntent {
|
|
179
|
+
action: string;
|
|
180
|
+
target: string;
|
|
181
|
+
risk: RiskLevel;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Result of intent check
|
|
186
|
+
*/
|
|
187
|
+
export interface IntentCheckResult {
|
|
188
|
+
violations: RuleMatch[];
|
|
189
|
+
warnings: RuleMatch[];
|
|
190
|
+
recommendations: RuleMatch[];
|
|
191
|
+
detectedIntents: DetectedIntent[];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ==================== PATTERNS ====================
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Error patterns analysis
|
|
198
|
+
*/
|
|
199
|
+
export interface ErrorPatterns {
|
|
200
|
+
byType: Record<string, number>;
|
|
201
|
+
bySeverity: Record<Severity, number>;
|
|
202
|
+
byFile: Record<string, number>;
|
|
203
|
+
byTime: Record<string, number>;
|
|
204
|
+
total: number;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Correction patterns analysis
|
|
209
|
+
*/
|
|
210
|
+
export interface CorrectionPatterns {
|
|
211
|
+
byCategory: Record<string, number>;
|
|
212
|
+
byFile: Record<string, number>;
|
|
213
|
+
total: number;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* High-frequency pattern candidate for auto-rule
|
|
218
|
+
*/
|
|
219
|
+
export interface HighFrequencyPattern {
|
|
220
|
+
pattern: string;
|
|
221
|
+
count: number;
|
|
222
|
+
type: 'error' | 'correction' | 'category';
|
|
223
|
+
suggestedRule?: AutoRule;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ==================== TRENDS ====================
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Weekly comparison data
|
|
230
|
+
*/
|
|
231
|
+
export interface TrendData {
|
|
232
|
+
period: {
|
|
233
|
+
from: string;
|
|
234
|
+
to: string;
|
|
235
|
+
};
|
|
236
|
+
thisWeek: {
|
|
237
|
+
errors: number;
|
|
238
|
+
successes: number;
|
|
239
|
+
};
|
|
240
|
+
lastWeek: {
|
|
241
|
+
errors: number;
|
|
242
|
+
successes: number;
|
|
243
|
+
};
|
|
244
|
+
trends: {
|
|
245
|
+
errorChange: number;
|
|
246
|
+
successChange: number;
|
|
247
|
+
};
|
|
248
|
+
healthStatus: HealthStatus;
|
|
249
|
+
analysis: {
|
|
250
|
+
errorDirection: 'decreasing' | 'stable' | 'increasing';
|
|
251
|
+
successDirection: 'decreasing' | 'stable' | 'increasing';
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Daily breakdown for charts
|
|
257
|
+
*/
|
|
258
|
+
export interface DailyBreakdown {
|
|
259
|
+
date: string;
|
|
260
|
+
errors: number;
|
|
261
|
+
successes: number;
|
|
262
|
+
ratio: number;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// ==================== SUCCESS/FAILURE RATIO ====================
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Success/failure ratio analysis
|
|
269
|
+
*/
|
|
270
|
+
export interface BalanceRatio {
|
|
271
|
+
ratio: number;
|
|
272
|
+
status: 'EXCELLENT' | 'GOOD' | 'LEARNING' | 'NEEDS_ATTENTION' | 'NO_DATA';
|
|
273
|
+
message: string;
|
|
274
|
+
failures: number;
|
|
275
|
+
successes: number;
|
|
276
|
+
breakdown: {
|
|
277
|
+
errors: number;
|
|
278
|
+
corrections: number;
|
|
279
|
+
successPatterns: number;
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ==================== ADAPTIVE ENGINE ====================
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Severity adjustment record
|
|
287
|
+
*/
|
|
288
|
+
export interface SeverityAdjustment {
|
|
289
|
+
id: string;
|
|
290
|
+
from: Severity;
|
|
291
|
+
to: Severity;
|
|
292
|
+
reason: string;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Promotion/demotion record
|
|
297
|
+
*/
|
|
298
|
+
export interface RulePromotion {
|
|
299
|
+
id: string;
|
|
300
|
+
pattern: string;
|
|
301
|
+
accuracy: number;
|
|
302
|
+
status: 'PROMOTED_TO_CORE' | 'DEMOTED_TO_LOW';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// ==================== SKILL INJECTION ====================
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Skill candidate for generation
|
|
309
|
+
*/
|
|
310
|
+
export interface SkillCandidate {
|
|
311
|
+
category: string;
|
|
312
|
+
count: number;
|
|
313
|
+
config: {
|
|
314
|
+
name: string;
|
|
315
|
+
description: string;
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Generated skill result
|
|
321
|
+
*/
|
|
322
|
+
export interface GeneratedSkill {
|
|
323
|
+
category: string;
|
|
324
|
+
name: string;
|
|
325
|
+
path: string;
|
|
326
|
+
lessonCount: number;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ==================== RULE SHARING ====================
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Exported rules in YAML format
|
|
333
|
+
*/
|
|
334
|
+
export interface ExportedRules {
|
|
335
|
+
metadata: {
|
|
336
|
+
project: string;
|
|
337
|
+
exportedAt: string;
|
|
338
|
+
totalRules: number;
|
|
339
|
+
};
|
|
340
|
+
lessons: Lesson[];
|
|
341
|
+
autoRules: AutoRule[];
|
|
342
|
+
highFrequency: HighFrequencyPattern[];
|
|
343
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Benchmark Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides tools for measuring and reporting performance:
|
|
5
|
+
* - Execution time measurement
|
|
6
|
+
* - Memory usage tracking
|
|
7
|
+
* - Performance reports
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { benchmark, measure, report } from './benchmark.js';
|
|
11
|
+
*
|
|
12
|
+
* const result = await measure('operation-name', async () => {
|
|
13
|
+
* // code to measure
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* report();
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { fileURLToPath } from 'url';
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
|
|
23
|
+
// ==================== STORAGE ====================
|
|
24
|
+
|
|
25
|
+
const metrics = new Map();
|
|
26
|
+
const thresholds = {
|
|
27
|
+
fast: 100, // < 100ms
|
|
28
|
+
normal: 500, // 100-500ms
|
|
29
|
+
slow: 1000, // 500-1000ms
|
|
30
|
+
// > 1000ms = very slow
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ==================== COLORS ====================
|
|
34
|
+
|
|
35
|
+
const c = {
|
|
36
|
+
reset: '\x1b[0m',
|
|
37
|
+
red: '\x1b[31m',
|
|
38
|
+
green: '\x1b[32m',
|
|
39
|
+
yellow: '\x1b[33m',
|
|
40
|
+
blue: '\x1b[34m',
|
|
41
|
+
cyan: '\x1b[36m',
|
|
42
|
+
gray: '\x1b[90m',
|
|
43
|
+
bold: '\x1b[1m'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// ==================== TIMING ====================
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Measure execution time of a function
|
|
50
|
+
* @param {string} name - Operation name
|
|
51
|
+
* @param {Function} fn - Function to measure
|
|
52
|
+
* @returns {Promise<{result: any, duration: number, memory: object}>}
|
|
53
|
+
*/
|
|
54
|
+
async function measure(name, fn) {
|
|
55
|
+
const startTime = performance.now();
|
|
56
|
+
const startMemory = process.memoryUsage();
|
|
57
|
+
|
|
58
|
+
let result;
|
|
59
|
+
let error = null;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
result = await fn();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
error = e;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const endTime = performance.now();
|
|
68
|
+
const endMemory = process.memoryUsage();
|
|
69
|
+
|
|
70
|
+
const duration = Math.round((endTime - startTime) * 100) / 100;
|
|
71
|
+
const memoryDelta = {
|
|
72
|
+
heapUsed: endMemory.heapUsed - startMemory.heapUsed,
|
|
73
|
+
heapTotal: endMemory.heapTotal - startMemory.heapTotal,
|
|
74
|
+
external: endMemory.external - startMemory.external
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Store metric
|
|
78
|
+
const metric = {
|
|
79
|
+
name,
|
|
80
|
+
duration,
|
|
81
|
+
memory: memoryDelta,
|
|
82
|
+
timestamp: new Date().toISOString(),
|
|
83
|
+
success: !error
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
if (!metrics.has(name)) {
|
|
87
|
+
metrics.set(name, []);
|
|
88
|
+
}
|
|
89
|
+
metrics.get(name).push(metric);
|
|
90
|
+
|
|
91
|
+
if (error) {
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return { result, duration, memory: memoryDelta };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Simple timing wrapper
|
|
100
|
+
* @param {string} label - Operation label
|
|
101
|
+
*/
|
|
102
|
+
function time(label) {
|
|
103
|
+
const start = performance.now();
|
|
104
|
+
return {
|
|
105
|
+
end: () => {
|
|
106
|
+
const duration = performance.now() - start;
|
|
107
|
+
console.log(`${c.gray}⏱️ ${label}: ${duration.toFixed(2)}ms${c.reset}`);
|
|
108
|
+
return duration;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Benchmark multiple iterations
|
|
115
|
+
* @param {string} name - Benchmark name
|
|
116
|
+
* @param {Function} fn - Function to benchmark
|
|
117
|
+
* @param {number} iterations - Number of iterations
|
|
118
|
+
*/
|
|
119
|
+
async function benchmark(name, fn, iterations = 10) {
|
|
120
|
+
const results = [];
|
|
121
|
+
|
|
122
|
+
for (let i = 0; i < iterations; i++) {
|
|
123
|
+
const start = performance.now();
|
|
124
|
+
await fn();
|
|
125
|
+
results.push(performance.now() - start);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const avg = results.reduce((a, b) => a + b, 0) / results.length;
|
|
129
|
+
const min = Math.min(...results);
|
|
130
|
+
const max = Math.max(...results);
|
|
131
|
+
const p95 = results.sort((a, b) => a - b)[Math.floor(results.length * 0.95)];
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
name,
|
|
135
|
+
iterations,
|
|
136
|
+
avg: Math.round(avg * 100) / 100,
|
|
137
|
+
min: Math.round(min * 100) / 100,
|
|
138
|
+
max: Math.round(max * 100) / 100,
|
|
139
|
+
p95: Math.round(p95 * 100) / 100
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ==================== MEMORY ====================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get current memory usage
|
|
147
|
+
*/
|
|
148
|
+
function getMemoryUsage() {
|
|
149
|
+
const usage = process.memoryUsage();
|
|
150
|
+
return {
|
|
151
|
+
heapUsed: formatBytes(usage.heapUsed),
|
|
152
|
+
heapTotal: formatBytes(usage.heapTotal),
|
|
153
|
+
external: formatBytes(usage.external),
|
|
154
|
+
rss: formatBytes(usage.rss)
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Format bytes to human readable
|
|
160
|
+
*/
|
|
161
|
+
function formatBytes(bytes) {
|
|
162
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
163
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
164
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ==================== REPORTING ====================
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get performance status color
|
|
171
|
+
*/
|
|
172
|
+
function getStatusColor(duration) {
|
|
173
|
+
if (duration < thresholds.fast) return c.green;
|
|
174
|
+
if (duration < thresholds.normal) return c.blue;
|
|
175
|
+
if (duration < thresholds.slow) return c.yellow;
|
|
176
|
+
return c.red;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get performance status label
|
|
181
|
+
*/
|
|
182
|
+
function getStatusLabel(duration) {
|
|
183
|
+
if (duration < thresholds.fast) return '🚀 FAST';
|
|
184
|
+
if (duration < thresholds.normal) return '✓ NORMAL';
|
|
185
|
+
if (duration < thresholds.slow) return '⚠️ SLOW';
|
|
186
|
+
return '❌ VERY SLOW';
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Print performance report
|
|
191
|
+
*/
|
|
192
|
+
function report() {
|
|
193
|
+
console.log(`\n${c.cyan}╔════════════════════════════════════════╗${c.reset}`);
|
|
194
|
+
console.log(`${c.cyan}║${c.reset} 📊 Performance Report ${c.cyan}║${c.reset}`);
|
|
195
|
+
console.log(`${c.cyan}╚════════════════════════════════════════╝${c.reset}\n`);
|
|
196
|
+
|
|
197
|
+
if (metrics.size === 0) {
|
|
198
|
+
console.log(`${c.gray}No metrics collected yet.${c.reset}`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for (const [name, data] of metrics) {
|
|
203
|
+
const durations = data.map(d => d.duration);
|
|
204
|
+
const avg = durations.reduce((a, b) => a + b, 0) / durations.length;
|
|
205
|
+
const color = getStatusColor(avg);
|
|
206
|
+
const status = getStatusLabel(avg);
|
|
207
|
+
|
|
208
|
+
console.log(`${c.bold}${name}${c.reset}`);
|
|
209
|
+
console.log(` Runs: ${data.length}`);
|
|
210
|
+
console.log(` Avg: ${color}${avg.toFixed(2)}ms${c.reset} ${status}`);
|
|
211
|
+
console.log(` Min: ${Math.min(...durations).toFixed(2)}ms`);
|
|
212
|
+
console.log(` Max: ${Math.max(...durations).toFixed(2)}ms`);
|
|
213
|
+
console.log('');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Memory summary
|
|
217
|
+
const mem = getMemoryUsage();
|
|
218
|
+
console.log(`${c.bold}Memory Usage${c.reset}`);
|
|
219
|
+
console.log(` Heap: ${mem.heapUsed} / ${mem.heapTotal}`);
|
|
220
|
+
console.log(` RSS: ${mem.rss}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get metrics as JSON
|
|
225
|
+
*/
|
|
226
|
+
function getMetrics() {
|
|
227
|
+
const result = {};
|
|
228
|
+
for (const [name, data] of metrics) {
|
|
229
|
+
const durations = data.map(d => d.duration);
|
|
230
|
+
result[name] = {
|
|
231
|
+
runs: data.length,
|
|
232
|
+
avg: durations.reduce((a, b) => a + b, 0) / durations.length,
|
|
233
|
+
min: Math.min(...durations),
|
|
234
|
+
max: Math.max(...durations),
|
|
235
|
+
success: data.filter(d => d.success).length
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
return result;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Clear all metrics
|
|
243
|
+
*/
|
|
244
|
+
function clear() {
|
|
245
|
+
metrics.clear();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ==================== EXPORTS ====================
|
|
249
|
+
|
|
250
|
+
export {
|
|
251
|
+
measure,
|
|
252
|
+
time,
|
|
253
|
+
benchmark,
|
|
254
|
+
getMemoryUsage,
|
|
255
|
+
report,
|
|
256
|
+
getMetrics,
|
|
257
|
+
clear,
|
|
258
|
+
thresholds
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export default {
|
|
262
|
+
measure,
|
|
263
|
+
time,
|
|
264
|
+
benchmark,
|
|
265
|
+
getMemoryUsage,
|
|
266
|
+
report,
|
|
267
|
+
getMetrics,
|
|
268
|
+
clear
|
|
269
|
+
};
|