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,781 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Pattern Analyzer - Analyze errors and corrections to find patterns
|
|
4
|
+
*
|
|
5
|
+
* Part of FAANG-Grade Auto-Learn System Phase 2
|
|
6
|
+
*
|
|
7
|
+
* Analyzes:
|
|
8
|
+
* - Frequency of error types
|
|
9
|
+
* - Common correction patterns
|
|
10
|
+
* - Hot files/directories
|
|
11
|
+
* - Time-based trends
|
|
12
|
+
*
|
|
13
|
+
* Generates:
|
|
14
|
+
* - Auto-rules for frequent patterns
|
|
15
|
+
* - Insights for improvement
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node pattern_analyzer.js --analyze
|
|
19
|
+
* node pattern_analyzer.js --rules
|
|
20
|
+
* node pattern_analyzer.js --insights
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fs from 'fs';
|
|
24
|
+
import path from 'path';
|
|
25
|
+
import { fileURLToPath } from 'url';
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = path.dirname(__filename);
|
|
29
|
+
|
|
30
|
+
// Colors
|
|
31
|
+
const c = {
|
|
32
|
+
reset: '\x1b[0m',
|
|
33
|
+
red: '\x1b[31m',
|
|
34
|
+
green: '\x1b[32m',
|
|
35
|
+
yellow: '\x1b[33m',
|
|
36
|
+
blue: '\x1b[34m',
|
|
37
|
+
cyan: '\x1b[36m',
|
|
38
|
+
magenta: '\x1b[35m',
|
|
39
|
+
gray: '\x1b[90m',
|
|
40
|
+
bold: '\x1b[1m'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Find project root
|
|
44
|
+
function findProjectRoot() {
|
|
45
|
+
let current = process.cwd();
|
|
46
|
+
while (current !== path.dirname(current)) {
|
|
47
|
+
if (fs.existsSync(path.join(current, '.agent'))) {
|
|
48
|
+
return current;
|
|
49
|
+
}
|
|
50
|
+
current = path.dirname(current);
|
|
51
|
+
}
|
|
52
|
+
return process.cwd();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const projectRoot = findProjectRoot();
|
|
56
|
+
const knowledgePath = path.join(projectRoot, '.agent', 'knowledge');
|
|
57
|
+
const errorsPath = path.join(knowledgePath, 'detected-errors.json');
|
|
58
|
+
const correctionsPath = path.join(knowledgePath, 'user-corrections.json');
|
|
59
|
+
const lessonsPath = path.join(knowledgePath, 'lessons-learned.json');
|
|
60
|
+
const successesPath = path.join(knowledgePath, 'successes.json');
|
|
61
|
+
const patternsPath = path.join(knowledgePath, 'patterns.json');
|
|
62
|
+
const autoRulesPath = path.join(knowledgePath, 'auto-rules.yaml');
|
|
63
|
+
|
|
64
|
+
// Load data files
|
|
65
|
+
function loadJson(filePath) {
|
|
66
|
+
try {
|
|
67
|
+
if (fs.existsSync(filePath)) {
|
|
68
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
69
|
+
}
|
|
70
|
+
} catch { }
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function loadErrors() {
|
|
75
|
+
const data = loadJson(errorsPath);
|
|
76
|
+
return data?.errors || [];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function loadCorrections() {
|
|
80
|
+
const data = loadJson(correctionsPath);
|
|
81
|
+
return data?.corrections || [];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function loadLessons() {
|
|
85
|
+
const data = loadJson(lessonsPath);
|
|
86
|
+
return data?.lessons || [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function loadSuccesses() {
|
|
90
|
+
const data = loadJson(successesPath);
|
|
91
|
+
return data?.successes || [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ==================== PATTERN ANALYSIS ====================
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Analyze error patterns
|
|
98
|
+
*/
|
|
99
|
+
function analyzeErrorPatterns(errors) {
|
|
100
|
+
const patterns = {
|
|
101
|
+
byType: {},
|
|
102
|
+
bySeverity: {},
|
|
103
|
+
bySource: {},
|
|
104
|
+
byFile: {},
|
|
105
|
+
byHour: {},
|
|
106
|
+
byDay: {}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
for (const err of errors) {
|
|
110
|
+
// By type
|
|
111
|
+
patterns.byType[err.type] = (patterns.byType[err.type] || 0) + 1;
|
|
112
|
+
|
|
113
|
+
// By severity
|
|
114
|
+
patterns.bySeverity[err.severity] = (patterns.bySeverity[err.severity] || 0) + 1;
|
|
115
|
+
|
|
116
|
+
// By source
|
|
117
|
+
patterns.bySource[err.source] = (patterns.bySource[err.source] || 0) + 1;
|
|
118
|
+
|
|
119
|
+
// By file (directory actually)
|
|
120
|
+
if (err.file) {
|
|
121
|
+
const dir = path.dirname(err.file);
|
|
122
|
+
patterns.byFile[dir] = (patterns.byFile[dir] || 0) + 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// By hour of day
|
|
126
|
+
if (err.timestamp) {
|
|
127
|
+
const hour = new Date(err.timestamp).getHours();
|
|
128
|
+
patterns.byHour[hour] = (patterns.byHour[hour] || 0) + 1;
|
|
129
|
+
|
|
130
|
+
const day = new Date(err.timestamp).toISOString().split('T')[0];
|
|
131
|
+
patterns.byDay[day] = (patterns.byDay[day] || 0) + 1;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return patterns;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Analyze correction patterns
|
|
140
|
+
*/
|
|
141
|
+
function analyzeCorrectionPatterns(corrections) {
|
|
142
|
+
const patterns = {
|
|
143
|
+
byCategory: {},
|
|
144
|
+
byFile: {},
|
|
145
|
+
bySeverity: {},
|
|
146
|
+
byPattern: {}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
for (const corr of corrections) {
|
|
150
|
+
// By category
|
|
151
|
+
patterns.byCategory[corr.category] = (patterns.byCategory[corr.category] || 0) + 1;
|
|
152
|
+
|
|
153
|
+
// By file
|
|
154
|
+
if (corr.file) {
|
|
155
|
+
const dir = path.dirname(corr.file);
|
|
156
|
+
patterns.byFile[dir] = (patterns.byFile[dir] || 0) + 1;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// By severity
|
|
160
|
+
patterns.bySeverity[corr.severity] = (patterns.bySeverity[corr.severity] || 0) + 1;
|
|
161
|
+
|
|
162
|
+
// By pattern name
|
|
163
|
+
patterns.byPattern[corr.pattern] = (patterns.byPattern[corr.pattern] || 0) + 1;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return patterns;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Analyze success patterns
|
|
171
|
+
*/
|
|
172
|
+
function analyzeSuccessPatterns(successes) {
|
|
173
|
+
const patterns = {
|
|
174
|
+
byType: {},
|
|
175
|
+
byPattern: {},
|
|
176
|
+
byFile: {},
|
|
177
|
+
total: successes.length
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
for (const s of successes) {
|
|
181
|
+
// By type
|
|
182
|
+
patterns.byType[s.type] = (patterns.byType[s.type] || 0) + 1;
|
|
183
|
+
|
|
184
|
+
// By pattern name
|
|
185
|
+
patterns.byPattern[s.pattern] = (patterns.byPattern[s.pattern] || 0) + 1;
|
|
186
|
+
|
|
187
|
+
// By file
|
|
188
|
+
if (s.file) {
|
|
189
|
+
const dir = path.dirname(s.file);
|
|
190
|
+
patterns.byFile[dir] = (patterns.byFile[dir] || 0) + 1;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return patterns;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Calculate success/failure ratio for balanced learning
|
|
199
|
+
*/
|
|
200
|
+
function calculateSuccessFailureRatio(errors, corrections, successes) {
|
|
201
|
+
const totalFailures = errors.length + corrections.length;
|
|
202
|
+
const totalSuccesses = successes.length;
|
|
203
|
+
const total = totalFailures + totalSuccesses;
|
|
204
|
+
|
|
205
|
+
if (total === 0) {
|
|
206
|
+
return {
|
|
207
|
+
ratio: 0,
|
|
208
|
+
status: 'NO_DATA',
|
|
209
|
+
message: 'No data to analyze',
|
|
210
|
+
failures: 0,
|
|
211
|
+
successes: 0
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const successRate = totalSuccesses / total;
|
|
216
|
+
let status, message;
|
|
217
|
+
|
|
218
|
+
if (successRate > 0.7) {
|
|
219
|
+
status = 'EXCELLENT';
|
|
220
|
+
message = 'Strong positive patterns, healthy codebase';
|
|
221
|
+
} else if (successRate > 0.5) {
|
|
222
|
+
status = 'GOOD';
|
|
223
|
+
message = 'More successes than failures, trending positive';
|
|
224
|
+
} else if (successRate > 0.3) {
|
|
225
|
+
status = 'LEARNING';
|
|
226
|
+
message = 'More failures than successes, system is learning';
|
|
227
|
+
} else {
|
|
228
|
+
status = 'NEEDS_ATTENTION';
|
|
229
|
+
message = 'Low success rate, focus on best practices';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
ratio: Math.round(successRate * 100) / 100,
|
|
234
|
+
status,
|
|
235
|
+
message,
|
|
236
|
+
failures: totalFailures,
|
|
237
|
+
successes: totalSuccesses,
|
|
238
|
+
breakdown: {
|
|
239
|
+
errors: errors.length,
|
|
240
|
+
corrections: corrections.length,
|
|
241
|
+
successPatterns: successes.length
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ==================== TREND ANALYSIS ====================
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Calculate trends from timestamped data
|
|
250
|
+
*/
|
|
251
|
+
function calculateTrends(errors, corrections, successes) {
|
|
252
|
+
const now = new Date();
|
|
253
|
+
const oneWeekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
|
|
254
|
+
const twoWeeksAgo = new Date(now - 14 * 24 * 60 * 60 * 1000);
|
|
255
|
+
|
|
256
|
+
// Filter by time period
|
|
257
|
+
const thisWeekErrors = errors.filter(e => new Date(e.timestamp) > oneWeekAgo).length;
|
|
258
|
+
const lastWeekErrors = errors.filter(e => {
|
|
259
|
+
const d = new Date(e.timestamp);
|
|
260
|
+
return d > twoWeeksAgo && d <= oneWeekAgo;
|
|
261
|
+
}).length;
|
|
262
|
+
|
|
263
|
+
const thisWeekSuccesses = successes.filter(s => {
|
|
264
|
+
const d = new Date(s.detectedAt || s.timestamp);
|
|
265
|
+
return d > oneWeekAgo;
|
|
266
|
+
}).length;
|
|
267
|
+
const lastWeekSuccesses = successes.filter(s => {
|
|
268
|
+
const d = new Date(s.detectedAt || s.timestamp);
|
|
269
|
+
return d > twoWeeksAgo && d <= oneWeekAgo;
|
|
270
|
+
}).length;
|
|
271
|
+
|
|
272
|
+
// Calculate trends
|
|
273
|
+
const errorTrend = lastWeekErrors > 0
|
|
274
|
+
? Math.round(((thisWeekErrors - lastWeekErrors) / lastWeekErrors) * 100)
|
|
275
|
+
: 0;
|
|
276
|
+
|
|
277
|
+
const successTrend = lastWeekSuccesses > 0
|
|
278
|
+
? Math.round(((thisWeekSuccesses - lastWeekSuccesses) / lastWeekSuccesses) * 100)
|
|
279
|
+
: 0;
|
|
280
|
+
|
|
281
|
+
// Determine overall health trend
|
|
282
|
+
let healthStatus;
|
|
283
|
+
if (errorTrend < 0 && successTrend >= 0) {
|
|
284
|
+
healthStatus = 'IMPROVING';
|
|
285
|
+
} else if (errorTrend > 20) {
|
|
286
|
+
healthStatus = 'DECLINING';
|
|
287
|
+
} else if (errorTrend <= 0) {
|
|
288
|
+
healthStatus = 'STABLE';
|
|
289
|
+
} else {
|
|
290
|
+
healthStatus = 'NEUTRAL';
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
period: {
|
|
295
|
+
from: twoWeeksAgo.toISOString().split('T')[0],
|
|
296
|
+
to: now.toISOString().split('T')[0]
|
|
297
|
+
},
|
|
298
|
+
thisWeek: {
|
|
299
|
+
errors: thisWeekErrors,
|
|
300
|
+
successes: thisWeekSuccesses
|
|
301
|
+
},
|
|
302
|
+
lastWeek: {
|
|
303
|
+
errors: lastWeekErrors,
|
|
304
|
+
successes: lastWeekSuccesses
|
|
305
|
+
},
|
|
306
|
+
trends: {
|
|
307
|
+
errorChange: errorTrend,
|
|
308
|
+
successChange: successTrend
|
|
309
|
+
},
|
|
310
|
+
healthStatus,
|
|
311
|
+
analysis: {
|
|
312
|
+
errorDirection: errorTrend < 0 ? 'decreasing' : errorTrend > 0 ? 'increasing' : 'stable',
|
|
313
|
+
successDirection: successTrend < 0 ? 'decreasing' : successTrend > 0 ? 'increasing' : 'stable'
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get daily breakdown for last 7 days
|
|
320
|
+
*/
|
|
321
|
+
function getDailyBreakdown(errors, successes) {
|
|
322
|
+
const days = [];
|
|
323
|
+
const now = new Date();
|
|
324
|
+
|
|
325
|
+
for (let i = 6; i >= 0; i--) {
|
|
326
|
+
const date = new Date(now - i * 24 * 60 * 60 * 1000);
|
|
327
|
+
const dateStr = date.toISOString().split('T')[0];
|
|
328
|
+
|
|
329
|
+
const dayErrors = errors.filter(e =>
|
|
330
|
+
e.timestamp && e.timestamp.startsWith(dateStr)
|
|
331
|
+
).length;
|
|
332
|
+
|
|
333
|
+
const daySuccesses = successes.filter(s =>
|
|
334
|
+
(s.detectedAt || s.timestamp || '').startsWith(dateStr)
|
|
335
|
+
).length;
|
|
336
|
+
|
|
337
|
+
days.push({
|
|
338
|
+
date: dateStr,
|
|
339
|
+
errors: dayErrors,
|
|
340
|
+
successes: daySuccesses,
|
|
341
|
+
ratio: dayErrors + daySuccesses > 0
|
|
342
|
+
? Math.round((daySuccesses / (dayErrors + daySuccesses)) * 100)
|
|
343
|
+
: 0
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return days;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Find high-frequency patterns (candidates for auto-rules)
|
|
352
|
+
*/
|
|
353
|
+
function findHighFrequencyPatterns(errorPatterns, correctionPatterns) {
|
|
354
|
+
const threshold = 3; // Minimum occurrences to be considered high-frequency
|
|
355
|
+
const highFrequency = [];
|
|
356
|
+
|
|
357
|
+
// Error patterns
|
|
358
|
+
for (const [type, count] of Object.entries(errorPatterns.byType)) {
|
|
359
|
+
if (count >= threshold) {
|
|
360
|
+
highFrequency.push({
|
|
361
|
+
source: 'errors',
|
|
362
|
+
type: type,
|
|
363
|
+
count: count,
|
|
364
|
+
severity: 'HIGH',
|
|
365
|
+
suggestedRule: generateRuleForError(type, count)
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Correction patterns
|
|
371
|
+
for (const [pattern, count] of Object.entries(correctionPatterns.byPattern)) {
|
|
372
|
+
if (count >= threshold) {
|
|
373
|
+
highFrequency.push({
|
|
374
|
+
source: 'corrections',
|
|
375
|
+
type: pattern,
|
|
376
|
+
count: count,
|
|
377
|
+
severity: 'HIGH',
|
|
378
|
+
suggestedRule: generateRuleForCorrection(pattern, count)
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Category patterns
|
|
384
|
+
for (const [category, count] of Object.entries(correctionPatterns.byCategory)) {
|
|
385
|
+
if (count >= threshold) {
|
|
386
|
+
highFrequency.push({
|
|
387
|
+
source: 'corrections',
|
|
388
|
+
type: `category:${category}`,
|
|
389
|
+
count: count,
|
|
390
|
+
severity: 'MEDIUM',
|
|
391
|
+
suggestedRule: generateRuleForCategory(category, count)
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return highFrequency.sort((a, b) => b.count - a.count);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Generate rule for error type
|
|
401
|
+
*/
|
|
402
|
+
function generateRuleForError(type, count) {
|
|
403
|
+
const rules = {
|
|
404
|
+
'test': {
|
|
405
|
+
id: 'AUTO-TEST',
|
|
406
|
+
name: 'Ensure tests pass before completion',
|
|
407
|
+
check: 'Run npm test before notify_user',
|
|
408
|
+
prevention: 'Always verify test suite passes'
|
|
409
|
+
},
|
|
410
|
+
'build': {
|
|
411
|
+
id: 'AUTO-BUILD',
|
|
412
|
+
name: 'Ensure TypeScript compiles',
|
|
413
|
+
check: 'Run npx tsc --noEmit before completion',
|
|
414
|
+
prevention: 'Check for type errors before proceeding'
|
|
415
|
+
},
|
|
416
|
+
'lint': {
|
|
417
|
+
id: 'AUTO-LINT',
|
|
418
|
+
name: 'Fix linting issues',
|
|
419
|
+
check: 'Run npm run lint before completion',
|
|
420
|
+
prevention: 'Follow ESLint rules strictly'
|
|
421
|
+
},
|
|
422
|
+
'pattern': {
|
|
423
|
+
id: 'AUTO-PATTERN',
|
|
424
|
+
name: 'Avoid anti-patterns',
|
|
425
|
+
check: 'Scan for console.error, empty catch',
|
|
426
|
+
prevention: 'Use proper error handling'
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
return rules[type] || {
|
|
431
|
+
id: `AUTO-${type.toUpperCase()}`,
|
|
432
|
+
name: `Address ${type} issues`,
|
|
433
|
+
check: `Monitor ${type} errors`,
|
|
434
|
+
prevention: `Reduce ${type} error frequency (currently ${count})`
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Generate rule for correction pattern
|
|
440
|
+
*/
|
|
441
|
+
function generateRuleForCorrection(pattern, count) {
|
|
442
|
+
const rules = {
|
|
443
|
+
'import_path_fix': {
|
|
444
|
+
id: 'AUTO-IMPORT',
|
|
445
|
+
name: 'Use correct import paths',
|
|
446
|
+
check: 'Verify import paths exist before writing',
|
|
447
|
+
prevention: 'Use relative imports from project root, check tsconfig paths'
|
|
448
|
+
},
|
|
449
|
+
'type_annotation_fix': {
|
|
450
|
+
id: 'AUTO-TYPE',
|
|
451
|
+
name: 'Add explicit type annotations',
|
|
452
|
+
check: 'Ensure function params and returns have types',
|
|
453
|
+
prevention: 'Always add TypeScript types, avoid any'
|
|
454
|
+
},
|
|
455
|
+
'null_check_added': {
|
|
456
|
+
id: 'AUTO-NULL',
|
|
457
|
+
name: 'Handle null/undefined',
|
|
458
|
+
check: 'Use optional chaining and nullish coalescing',
|
|
459
|
+
prevention: 'Always handle potential null values with ?. and ??'
|
|
460
|
+
},
|
|
461
|
+
'async_await_fix': {
|
|
462
|
+
id: 'AUTO-ASYNC',
|
|
463
|
+
name: 'Proper async/await usage',
|
|
464
|
+
check: 'Verify async functions are awaited',
|
|
465
|
+
prevention: 'Never forget await, don\'t mix callbacks and promises'
|
|
466
|
+
},
|
|
467
|
+
'error_handling_added': {
|
|
468
|
+
id: 'AUTO-ERROR',
|
|
469
|
+
name: 'Add error handling',
|
|
470
|
+
check: 'Wrap async operations in try/catch',
|
|
471
|
+
prevention: 'Always handle errors, never use empty catch'
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
return rules[pattern] || {
|
|
476
|
+
id: `AUTO-${pattern.toUpperCase().replace(/_/g, '-')}`,
|
|
477
|
+
name: `Fix ${pattern.replace(/_/g, ' ')}`,
|
|
478
|
+
check: `Monitor for ${pattern} issues`,
|
|
479
|
+
prevention: `Reduce ${pattern} corrections (currently ${count})`
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Generate rule for category
|
|
485
|
+
*/
|
|
486
|
+
function generateRuleForCategory(category, count) {
|
|
487
|
+
const categoryRules = {
|
|
488
|
+
'imports': 'Double-check all import paths before writing code',
|
|
489
|
+
'types': 'Add explicit TypeScript types to all functions',
|
|
490
|
+
'null-safety': 'Use optional chaining (?.) and nullish coalescing (??)',
|
|
491
|
+
'async': 'Always use async/await, never mix with callbacks',
|
|
492
|
+
'error-handling': 'Wrap all async operations in try/catch',
|
|
493
|
+
'naming': 'Follow naming conventions: camelCase for vars, PascalCase for types',
|
|
494
|
+
'logic': 'Review conditional logic and add edge case tests'
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
id: `AUTO-CAT-${category.toUpperCase()}`,
|
|
499
|
+
name: `${category} best practices`,
|
|
500
|
+
check: `Review ${category} patterns`,
|
|
501
|
+
prevention: categoryRules[category] || `Follow ${category} best practices`
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// ==================== AUTO-RULES GENERATION ====================
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Generate auto-rules YAML file
|
|
509
|
+
*/
|
|
510
|
+
function generateAutoRules(highFrequencyPatterns) {
|
|
511
|
+
const existingLessons = loadLessons();
|
|
512
|
+
const existingPatterns = existingLessons.map(l => l.pattern);
|
|
513
|
+
|
|
514
|
+
// Filter out patterns that already have lessons
|
|
515
|
+
const newPatterns = highFrequencyPatterns.filter(p =>
|
|
516
|
+
!existingPatterns.some(existing => existing.includes(p.type))
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
const yaml = `# Auto-Generated Rules
|
|
520
|
+
# Generated: ${new Date().toISOString()}
|
|
521
|
+
# Based on: ${highFrequencyPatterns.length} high-frequency patterns
|
|
522
|
+
# New rules: ${newPatterns.length}
|
|
523
|
+
|
|
524
|
+
# These rules are auto-generated from error and correction patterns.
|
|
525
|
+
# Review and move approved rules to lessons-learned.yaml
|
|
526
|
+
|
|
527
|
+
rules:
|
|
528
|
+
${newPatterns.map(p => `
|
|
529
|
+
- id: ${p.suggestedRule.id}
|
|
530
|
+
source: ${p.source}
|
|
531
|
+
pattern: "${p.type}"
|
|
532
|
+
frequency: ${p.count}
|
|
533
|
+
severity: ${p.severity}
|
|
534
|
+
name: "${p.suggestedRule.name}"
|
|
535
|
+
check: "${p.suggestedRule.check}"
|
|
536
|
+
prevention: "${p.suggestedRule.prevention}"
|
|
537
|
+
status: pending # pending | approved | rejected
|
|
538
|
+
created: "${new Date().toISOString()}"
|
|
539
|
+
`).join('')}
|
|
540
|
+
`;
|
|
541
|
+
|
|
542
|
+
fs.writeFileSync(autoRulesPath, yaml);
|
|
543
|
+
return newPatterns;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Save patterns analysis to JSON
|
|
548
|
+
*/
|
|
549
|
+
function savePatterns(errorPatterns, correctionPatterns, highFrequency) {
|
|
550
|
+
const data = {
|
|
551
|
+
_comment: "Pattern analysis from error_sensor and user_correction_sensor",
|
|
552
|
+
analyzedAt: new Date().toISOString(),
|
|
553
|
+
errors: errorPatterns,
|
|
554
|
+
corrections: correctionPatterns,
|
|
555
|
+
highFrequency: highFrequency.map(p => ({
|
|
556
|
+
type: p.type,
|
|
557
|
+
count: p.count,
|
|
558
|
+
source: p.source,
|
|
559
|
+
suggestedRuleId: p.suggestedRule.id
|
|
560
|
+
}))
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
fs.writeFileSync(patternsPath, JSON.stringify(data, null, 2));
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// ==================== INSIGHTS ====================
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Generate insights from patterns
|
|
570
|
+
*/
|
|
571
|
+
function generateInsights(errorPatterns, correctionPatterns, highFrequency) {
|
|
572
|
+
const insights = [];
|
|
573
|
+
|
|
574
|
+
// Insight 1: Most common error type
|
|
575
|
+
const topError = Object.entries(errorPatterns.byType).sort((a, b) => b[1] - a[1])[0];
|
|
576
|
+
if (topError) {
|
|
577
|
+
insights.push({
|
|
578
|
+
type: 'error_hotspot',
|
|
579
|
+
title: 'Most Common Error Type',
|
|
580
|
+
message: `"${topError[0]}" errors occur most frequently (${topError[1]} times)`,
|
|
581
|
+
recommendation: `Focus on reducing ${topError[0]} errors first`,
|
|
582
|
+
severity: 'HIGH'
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Insight 2: Most corrected category
|
|
587
|
+
const topCorrection = Object.entries(correctionPatterns.byCategory).sort((a, b) => b[1] - a[1])[0];
|
|
588
|
+
if (topCorrection) {
|
|
589
|
+
insights.push({
|
|
590
|
+
type: 'correction_hotspot',
|
|
591
|
+
title: 'Most Corrected Category',
|
|
592
|
+
message: `"${topCorrection[0]}" corrections happen most often (${topCorrection[1]} times)`,
|
|
593
|
+
recommendation: `AI should pay more attention to ${topCorrection[0]}`,
|
|
594
|
+
severity: 'HIGH'
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Insight 3: Hot directories
|
|
599
|
+
const allFiles = { ...errorPatterns.byFile, ...correctionPatterns.byFile };
|
|
600
|
+
const topDir = Object.entries(allFiles).sort((a, b) => b[1] - a[1])[0];
|
|
601
|
+
if (topDir) {
|
|
602
|
+
insights.push({
|
|
603
|
+
type: 'hot_directory',
|
|
604
|
+
title: 'Hot Directory',
|
|
605
|
+
message: `"${topDir[0]}" has the most issues (${topDir[1]} combined)`,
|
|
606
|
+
recommendation: `Extra care needed when modifying files in ${topDir[0]}`,
|
|
607
|
+
severity: 'MEDIUM'
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Insight 4: Rule candidates
|
|
612
|
+
const ruleCandidates = highFrequency.filter(p => p.count >= 5);
|
|
613
|
+
if (ruleCandidates.length > 0) {
|
|
614
|
+
insights.push({
|
|
615
|
+
type: 'rule_candidates',
|
|
616
|
+
title: 'Strong Rule Candidates',
|
|
617
|
+
message: `${ruleCandidates.length} patterns occurred 5+ times`,
|
|
618
|
+
recommendation: `Consider promoting these to permanent rules: ${ruleCandidates.map(r => r.type).join(', ')}`,
|
|
619
|
+
severity: 'HIGH'
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Insight 5: Peak error hours
|
|
624
|
+
const peakHour = Object.entries(errorPatterns.byHour).sort((a, b) => b[1] - a[1])[0];
|
|
625
|
+
if (peakHour) {
|
|
626
|
+
insights.push({
|
|
627
|
+
type: 'peak_hours',
|
|
628
|
+
title: 'Peak Error Time',
|
|
629
|
+
message: `Most errors occur around ${peakHour[0]}:00 (${peakHour[1]} errors)`,
|
|
630
|
+
recommendation: 'Consider taking breaks or extra care during this time',
|
|
631
|
+
severity: 'LOW'
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return insights;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// ==================== MAIN ====================
|
|
639
|
+
|
|
640
|
+
function runAnalysis() {
|
|
641
|
+
console.log(`${c.cyan}📊 Pattern Analyzer${c.reset}\n`);
|
|
642
|
+
|
|
643
|
+
const errors = loadErrors();
|
|
644
|
+
const corrections = loadCorrections();
|
|
645
|
+
|
|
646
|
+
console.log(`${c.gray}Data loaded:${c.reset}`);
|
|
647
|
+
console.log(` Errors: ${errors.length}`);
|
|
648
|
+
console.log(` Corrections: ${corrections.length}\n`);
|
|
649
|
+
|
|
650
|
+
if (errors.length === 0 && corrections.length === 0) {
|
|
651
|
+
console.log(`${c.yellow}No data to analyze.${c.reset}`);
|
|
652
|
+
console.log(`${c.gray}Run error_sensor.js and user_correction_sensor.js first.${c.reset}`);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Analyze patterns
|
|
657
|
+
const errorPatterns = analyzeErrorPatterns(errors);
|
|
658
|
+
const correctionPatterns = analyzeCorrectionPatterns(corrections);
|
|
659
|
+
const highFrequency = findHighFrequencyPatterns(errorPatterns, correctionPatterns);
|
|
660
|
+
|
|
661
|
+
// Save analysis
|
|
662
|
+
savePatterns(errorPatterns, correctionPatterns, highFrequency);
|
|
663
|
+
|
|
664
|
+
console.log(`${c.cyan}════════════════════════════════════════${c.reset}`);
|
|
665
|
+
console.log(`${c.bold}Analysis Complete${c.reset}\n`);
|
|
666
|
+
|
|
667
|
+
// Error patterns
|
|
668
|
+
console.log(`${c.bold}Error Patterns:${c.reset}`);
|
|
669
|
+
for (const [type, count] of Object.entries(errorPatterns.byType).sort((a, b) => b[1] - a[1])) {
|
|
670
|
+
const bar = '█'.repeat(Math.min(count, 20));
|
|
671
|
+
console.log(` ${type.padEnd(12)} ${c.red}${bar}${c.reset} ${count}`);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Correction patterns
|
|
675
|
+
console.log(`\n${c.bold}Correction Patterns:${c.reset}`);
|
|
676
|
+
for (const [cat, count] of Object.entries(correctionPatterns.byCategory).sort((a, b) => b[1] - a[1])) {
|
|
677
|
+
const bar = '█'.repeat(Math.min(count, 20));
|
|
678
|
+
console.log(` ${cat.padEnd(15)} ${c.blue}${bar}${c.reset} ${count}`);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// High frequency
|
|
682
|
+
if (highFrequency.length > 0) {
|
|
683
|
+
console.log(`\n${c.bold}${c.yellow}⚠️ High-Frequency Patterns (candidates for rules):${c.reset}`);
|
|
684
|
+
for (const p of highFrequency.slice(0, 5)) {
|
|
685
|
+
console.log(` ${c.yellow}●${c.reset} ${p.type}: ${p.count} times → ${p.suggestedRule.id}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
console.log(`\n${c.gray}Saved to: ${patternsPath}${c.reset}`);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function generateRules() {
|
|
693
|
+
console.log(`${c.cyan}🔧 Generating Auto-Rules${c.reset}\n`);
|
|
694
|
+
|
|
695
|
+
const errors = loadErrors();
|
|
696
|
+
const corrections = loadCorrections();
|
|
697
|
+
|
|
698
|
+
const errorPatterns = analyzeErrorPatterns(errors);
|
|
699
|
+
const correctionPatterns = analyzeCorrectionPatterns(corrections);
|
|
700
|
+
const highFrequency = findHighFrequencyPatterns(errorPatterns, correctionPatterns);
|
|
701
|
+
|
|
702
|
+
if (highFrequency.length === 0) {
|
|
703
|
+
console.log(`${c.yellow}No high-frequency patterns found.${c.reset}`);
|
|
704
|
+
console.log(`${c.gray}Need at least 3 occurrences of a pattern.${c.reset}`);
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
const newRules = generateAutoRules(highFrequency);
|
|
709
|
+
|
|
710
|
+
console.log(`${c.green}✓ Generated ${newRules.length} auto-rules${c.reset}`);
|
|
711
|
+
console.log(`${c.gray}Saved to: ${autoRulesPath}${c.reset}\n`);
|
|
712
|
+
|
|
713
|
+
console.log(`${c.bold}Generated Rules:${c.reset}`);
|
|
714
|
+
for (const rule of newRules) {
|
|
715
|
+
console.log(` ${c.cyan}${rule.suggestedRule.id}${c.reset}: ${rule.suggestedRule.name}`);
|
|
716
|
+
console.log(` ${c.gray}Prevention: ${rule.suggestedRule.prevention}${c.reset}`);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
console.log(`\n${c.yellow}Note: Review and approve rules in auto-rules.yaml${c.reset}`);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
function showInsights() {
|
|
723
|
+
console.log(`${c.cyan}💡 Pattern Insights${c.reset}\n`);
|
|
724
|
+
|
|
725
|
+
const errors = loadErrors();
|
|
726
|
+
const corrections = loadCorrections();
|
|
727
|
+
|
|
728
|
+
const errorPatterns = analyzeErrorPatterns(errors);
|
|
729
|
+
const correctionPatterns = analyzeCorrectionPatterns(corrections);
|
|
730
|
+
const highFrequency = findHighFrequencyPatterns(errorPatterns, correctionPatterns);
|
|
731
|
+
|
|
732
|
+
const insights = generateInsights(errorPatterns, correctionPatterns, highFrequency);
|
|
733
|
+
|
|
734
|
+
if (insights.length === 0) {
|
|
735
|
+
console.log(`${c.yellow}Not enough data for insights.${c.reset}`);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
for (const insight of insights) {
|
|
740
|
+
const sevColor = { HIGH: c.red, MEDIUM: c.yellow, LOW: c.gray }[insight.severity];
|
|
741
|
+
console.log(`${c.bold}${insight.title}${c.reset} ${sevColor}[${insight.severity}]${c.reset}`);
|
|
742
|
+
console.log(` ${insight.message}`);
|
|
743
|
+
console.log(` ${c.green}→ ${insight.recommendation}${c.reset}\n`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Parse CLI args
|
|
748
|
+
const args = process.argv.slice(2);
|
|
749
|
+
|
|
750
|
+
if (args.includes('--analyze') || args.includes('-a')) {
|
|
751
|
+
runAnalysis();
|
|
752
|
+
} else if (args.includes('--rules') || args.includes('-r')) {
|
|
753
|
+
generateRules();
|
|
754
|
+
} else if (args.includes('--insights') || args.includes('-i')) {
|
|
755
|
+
showInsights();
|
|
756
|
+
} else {
|
|
757
|
+
console.log(`${c.cyan}pattern_analyzer - Analyze patterns and generate rules${c.reset}
|
|
758
|
+
|
|
759
|
+
${c.bold}Usage:${c.reset}
|
|
760
|
+
node pattern_analyzer.js --analyze Analyze error and correction patterns
|
|
761
|
+
node pattern_analyzer.js --rules Generate auto-rules from patterns
|
|
762
|
+
node pattern_analyzer.js --insights Show insights and recommendations
|
|
763
|
+
|
|
764
|
+
${c.bold}Input files:${c.reset}
|
|
765
|
+
.agent/knowledge/detected-errors.json (from error_sensor)
|
|
766
|
+
.agent/knowledge/user-corrections.json (from correction_sensor)
|
|
767
|
+
|
|
768
|
+
${c.bold}Output files:${c.reset}
|
|
769
|
+
.agent/knowledge/patterns.json Analysis results
|
|
770
|
+
.agent/knowledge/auto-rules.yaml Generated rules
|
|
771
|
+
|
|
772
|
+
${c.bold}Example workflow:${c.reset}
|
|
773
|
+
1. Run error_sensor.js --scan all
|
|
774
|
+
2. Run user_correction_sensor.js --scan
|
|
775
|
+
3. Run pattern_analyzer.js --analyze
|
|
776
|
+
4. Run pattern_analyzer.js --rules
|
|
777
|
+
5. Review auto-rules.yaml and approve
|
|
778
|
+
`);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
export { runAnalysis, analyzeErrorPatterns, analyzeCorrectionPatterns, findHighFrequencyPatterns, generateInsights };
|