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.
Files changed (78) hide show
  1. package/README.md +179 -119
  2. package/bin/lib/commands/help.js +0 -4
  3. package/bin/lib/commands/install.js +129 -9
  4. package/bin/lib/ui.js +1 -1
  5. package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
  6. package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
  7. package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
  8. package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
  9. package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
  10. package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
  11. package/lib/agent-cli/bin/{ag-smart.js → agent.js} +48 -15
  12. package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
  13. package/lib/agent-cli/dashboard/index.html +538 -0
  14. package/lib/agent-cli/lib/audit.js +2 -2
  15. package/lib/agent-cli/lib/auto-learn.js +8 -8
  16. package/lib/agent-cli/lib/eslint-fix.js +1 -1
  17. package/lib/agent-cli/lib/fix.js +5 -5
  18. package/lib/agent-cli/lib/hooks/install-hooks.js +4 -4
  19. package/lib/agent-cli/lib/hooks/lint-learn.js +4 -4
  20. package/lib/agent-cli/lib/learn.js +10 -10
  21. package/lib/agent-cli/lib/recall.js +1 -1
  22. package/lib/agent-cli/lib/settings.js +24 -0
  23. package/lib/agent-cli/lib/skill-learn.js +2 -2
  24. package/lib/agent-cli/lib/stats.js +3 -3
  25. package/lib/agent-cli/lib/ui/dashboard-ui.js +103 -4
  26. package/lib/agent-cli/lib/ui/index.js +36 -6
  27. package/lib/agent-cli/lib/watcher.js +2 -2
  28. package/lib/agent-cli/package.json +4 -4
  29. package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
  30. package/lib/agent-cli/scripts/dashboard_server.js +224 -0
  31. package/lib/agent-cli/scripts/error_sensor.js +565 -0
  32. package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
  33. package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
  34. package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
  35. package/lib/agent-cli/scripts/rule_sharing.js +374 -0
  36. package/lib/agent-cli/scripts/skill_injector.js +387 -0
  37. package/lib/agent-cli/scripts/success_sensor.js +500 -0
  38. package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
  39. package/lib/agent-cli/services/auto-learn-service.js +247 -0
  40. package/lib/agent-cli/src/MIGRATION.md +418 -0
  41. package/lib/agent-cli/src/README.md +367 -0
  42. package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
  43. package/lib/agent-cli/src/core/evolution/index.js +17 -0
  44. package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
  45. package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
  46. package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
  47. package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
  48. package/lib/agent-cli/src/core/index.js +15 -0
  49. package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
  50. package/lib/agent-cli/src/core/learning/index.js +12 -0
  51. package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
  52. package/lib/agent-cli/src/core/scanning/index.js +14 -0
  53. package/lib/agent-cli/src/data/index.js +13 -0
  54. package/lib/agent-cli/src/data/repositories/index.js +8 -0
  55. package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
  56. package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
  57. package/lib/agent-cli/src/data/storage/index.js +8 -0
  58. package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
  59. package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
  60. package/lib/agent-cli/src/infrastructure/index.js +13 -0
  61. package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
  62. package/lib/agent-cli/src/services/export-service.js +162 -0
  63. package/lib/agent-cli/src/services/index.js +13 -0
  64. package/lib/agent-cli/src/services/learning-service.js +99 -0
  65. package/lib/agent-cli/types/index.d.ts +343 -0
  66. package/lib/agent-cli/utils/benchmark.js +269 -0
  67. package/lib/agent-cli/utils/logger.js +303 -0
  68. package/lib/agent-cli/utils/ml_patterns.js +300 -0
  69. package/lib/agent-cli/utils/recovery.js +312 -0
  70. package/lib/agent-cli/utils/telemetry.js +290 -0
  71. package/lib/agentskillskit-cli/ag-smart.js +15 -15
  72. package/lib/agentskillskit-cli/package.json +3 -3
  73. package/package.json +12 -6
  74. package/lib/agent-cli/lib/auto_preview.py +0 -148
  75. package/lib/agent-cli/lib/checklist.py +0 -222
  76. package/lib/agent-cli/lib/session_manager.py +0 -120
  77. package/lib/agent-cli/lib/verify_all.py +0 -327
  78. /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 };