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,190 @@
1
+ /**
2
+ * Unit Tests for adaptive_engine.js
3
+ *
4
+ * Tests:
5
+ * - adjustSeverity
6
+ * - promoteRules
7
+ * - demoteRules
8
+ * - analyzeRules
9
+ * - runAdaptiveCycle
10
+ */
11
+
12
+ import { describe, it, expect, vi } from 'vitest';
13
+
14
+ // Mock fs module
15
+ vi.mock('fs', async () => {
16
+ const actual = await vi.importActual('fs');
17
+ return {
18
+ ...actual,
19
+ default: {
20
+ ...actual,
21
+ existsSync: vi.fn(() => false),
22
+ readFileSync: vi.fn(() => '{}'),
23
+ writeFileSync: vi.fn()
24
+ },
25
+ existsSync: vi.fn(() => false),
26
+ readFileSync: vi.fn(() => '{}')
27
+ };
28
+ });
29
+
30
+ // Severity levels constant
31
+ const SEVERITY_LEVELS = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
32
+
33
+ describe('Adaptive Engine', () => {
34
+ describe('adjustSeverity', () => {
35
+ it('should increase severity for 10+ hits', () => {
36
+ const lesson = {
37
+ id: 'TEST-001',
38
+ hitCount: 15,
39
+ severity: 'MEDIUM'
40
+ };
41
+
42
+ const currentIndex = SEVERITY_LEVELS.indexOf(lesson.severity);
43
+
44
+ if (lesson.hitCount >= 10 && currentIndex < SEVERITY_LEVELS.length - 1) {
45
+ lesson.severity = SEVERITY_LEVELS[currentIndex + 1];
46
+ }
47
+
48
+ expect(lesson.severity).toBe('HIGH');
49
+ });
50
+
51
+ it('should increase to CRITICAL for 25+ hits', () => {
52
+ const lesson = {
53
+ id: 'TEST-001',
54
+ hitCount: 30,
55
+ severity: 'LOW'
56
+ };
57
+
58
+ const currentIndex = SEVERITY_LEVELS.indexOf(lesson.severity);
59
+
60
+ if (lesson.hitCount >= 25 && currentIndex < SEVERITY_LEVELS.length - 2) {
61
+ lesson.severity = SEVERITY_LEVELS[currentIndex + 2];
62
+ }
63
+
64
+ expect(lesson.severity).toBe('HIGH');
65
+ });
66
+
67
+ it('should decrease severity for 0 hits in 30 days', () => {
68
+ const thirtyDaysAgo = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000);
69
+ const lesson = {
70
+ id: 'TEST-001',
71
+ hitCount: 0,
72
+ severity: 'HIGH',
73
+ lastHit: thirtyDaysAgo.toISOString()
74
+ };
75
+
76
+ const lastHit = new Date(lesson.lastHit);
77
+ const thresholdDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
78
+ const currentIndex = SEVERITY_LEVELS.indexOf(lesson.severity);
79
+
80
+ if (lastHit < thresholdDate && currentIndex > 0) {
81
+ lesson.severity = SEVERITY_LEVELS[currentIndex - 1];
82
+ }
83
+
84
+ expect(lesson.severity).toBe('MEDIUM');
85
+ });
86
+
87
+ it('should not decrease LOW severity further', () => {
88
+ const lesson = {
89
+ id: 'TEST-001',
90
+ severity: 'LOW'
91
+ };
92
+
93
+ const currentIndex = SEVERITY_LEVELS.indexOf(lesson.severity);
94
+ expect(currentIndex).toBe(0);
95
+ });
96
+ });
97
+
98
+ describe('promoteRules', () => {
99
+ it('should promote rules with 95%+ accuracy', () => {
100
+ const lesson = {
101
+ id: 'TEST-001',
102
+ hitCount: 1,
103
+ successCount: 19, // 19/20 = 95%
104
+ severity: 'MEDIUM'
105
+ };
106
+
107
+ const totalAttempts = lesson.hitCount + lesson.successCount;
108
+ const accuracy = lesson.successCount / totalAttempts;
109
+
110
+ if (accuracy >= 0.95 && lesson.severity !== 'CRITICAL') {
111
+ lesson.promoted = true;
112
+ lesson.severity = 'CRITICAL';
113
+ }
114
+
115
+ expect(lesson.promoted).toBe(true);
116
+ expect(lesson.severity).toBe('CRITICAL');
117
+ });
118
+
119
+ it('should not promote rules with <10 attempts', () => {
120
+ const lesson = {
121
+ id: 'TEST-001',
122
+ hitCount: 1,
123
+ successCount: 5, // Only 6 attempts
124
+ severity: 'MEDIUM'
125
+ };
126
+
127
+ const totalAttempts = lesson.hitCount + lesson.successCount;
128
+
129
+ // Need 10+ attempts to evaluate
130
+ const shouldEvaluate = totalAttempts >= 10;
131
+
132
+ expect(shouldEvaluate).toBe(false);
133
+ });
134
+ });
135
+
136
+ describe('demoteRules', () => {
137
+ it('should demote rules with <50% accuracy', () => {
138
+ const lesson = {
139
+ id: 'TEST-001',
140
+ hitCount: 8,
141
+ successCount: 2, // 2/10 = 20%
142
+ severity: 'HIGH'
143
+ };
144
+
145
+ const totalAttempts = lesson.hitCount + lesson.successCount;
146
+ const accuracy = lesson.successCount / totalAttempts;
147
+
148
+ if (accuracy < 0.50 && lesson.severity !== 'LOW') {
149
+ lesson.demoted = true;
150
+ lesson.severity = 'LOW';
151
+ }
152
+
153
+ expect(lesson.demoted).toBe(true);
154
+ expect(lesson.severity).toBe('LOW');
155
+ });
156
+
157
+ it('should not demote LOW severity rules', () => {
158
+ const lesson = {
159
+ id: 'TEST-001',
160
+ severity: 'LOW'
161
+ };
162
+
163
+ const shouldDemote = lesson.severity !== 'LOW';
164
+
165
+ expect(shouldDemote).toBe(false);
166
+ });
167
+ });
168
+
169
+ describe('analyzeRules', () => {
170
+ it('should group lessons by severity', () => {
171
+ const lessons = [
172
+ { severity: 'LOW' },
173
+ { severity: 'MEDIUM' },
174
+ { severity: 'MEDIUM' },
175
+ { severity: 'HIGH' },
176
+ { severity: 'CRITICAL' }
177
+ ];
178
+
179
+ const bySeverity = {};
180
+ for (const lesson of lessons) {
181
+ bySeverity[lesson.severity] = (bySeverity[lesson.severity] || 0) + 1;
182
+ }
183
+
184
+ expect(bySeverity.LOW).toBe(1);
185
+ expect(bySeverity.MEDIUM).toBe(2);
186
+ expect(bySeverity.HIGH).toBe(1);
187
+ expect(bySeverity.CRITICAL).toBe(1);
188
+ });
189
+ });
190
+ });
@@ -0,0 +1,222 @@
1
+ /**
2
+ * E2E Integration Tests - Cross-Script Integration
3
+ *
4
+ * Tests integration between different scripts:
5
+ * - error_sensor ↔ pattern_analyzer
6
+ * - pattern_analyzer ↔ pre_execution_check
7
+ * - adaptive_engine ↔ skill_injector
8
+ */
9
+
10
+ import { describe, it, expect, vi } from 'vitest';
11
+
12
+ describe('E2E: Cross-Script Integration', () => {
13
+ describe('Error Sensor → Pattern Analyzer', () => {
14
+ it('should pass error format between scripts', () => {
15
+ // Error format from error_sensor
16
+ const sensorOutput = {
17
+ id: 'ERR-001',
18
+ type: 'test-failure',
19
+ source: 'test',
20
+ message: 'Test failed',
21
+ severity: 'HIGH',
22
+ timestamp: new Date().toISOString(),
23
+ context: {
24
+ project: { projectType: 'nextjs' },
25
+ file: { fileType: '.tsx' }
26
+ }
27
+ };
28
+
29
+ // Pattern analyzer input format
30
+ const analyzerInput = [sensorOutput];
31
+
32
+ // Verify format compatibility
33
+ expect(analyzerInput[0].type).toBeDefined();
34
+ expect(analyzerInput[0].severity).toBeDefined();
35
+ expect(analyzerInput[0].context).toBeDefined();
36
+ });
37
+
38
+ it('should aggregate errors correctly', () => {
39
+ const errors = [
40
+ { type: 'lint', severity: 'LOW' },
41
+ { type: 'lint', severity: 'LOW' },
42
+ { type: 'test', severity: 'HIGH' },
43
+ { type: 'lint', severity: 'LOW' }
44
+ ];
45
+
46
+ // Pattern analyzer aggregation
47
+ const byType = {};
48
+ const bySeverity = {};
49
+
50
+ for (const err of errors) {
51
+ byType[err.type] = (byType[err.type] || 0) + 1;
52
+ bySeverity[err.severity] = (bySeverity[err.severity] || 0) + 1;
53
+ }
54
+
55
+ expect(byType.lint).toBe(3);
56
+ expect(byType.test).toBe(1);
57
+ expect(bySeverity.LOW).toBe(3);
58
+ expect(bySeverity.HIGH).toBe(1);
59
+ });
60
+ });
61
+
62
+ describe('Pattern Analyzer → Pre-Execution Check', () => {
63
+ it('should generate rules compatible with checker', () => {
64
+ // Pattern analyzer output
65
+ const highFrequency = [
66
+ { pattern: 'missing-await', count: 5, type: 'error' },
67
+ { pattern: 'no-try-catch', count: 4, type: 'error' }
68
+ ];
69
+
70
+ // Generate rules for pre-execution check
71
+ const rules = highFrequency.map(hf => ({
72
+ id: `AUTO-${hf.pattern.toUpperCase()}`,
73
+ pattern: hf.pattern,
74
+ prevention: `Avoid ${hf.pattern}`,
75
+ severity: hf.count >= 5 ? 'HIGH' : 'MEDIUM',
76
+ status: 'proposed'
77
+ }));
78
+
79
+ expect(rules[0].id).toBe('AUTO-MISSING-AWAIT');
80
+ expect(rules[0].severity).toBe('HIGH');
81
+ expect(rules[1].severity).toBe('MEDIUM');
82
+ });
83
+
84
+ it('should match rules against intent', () => {
85
+ const rules = [
86
+ { pattern: 'delete', severity: 'CRITICAL' },
87
+ { pattern: 'async', severity: 'MEDIUM' }
88
+ ];
89
+
90
+ const intent = 'create async function';
91
+ const matches = rules.filter(r =>
92
+ intent.toLowerCase().includes(r.pattern)
93
+ );
94
+
95
+ expect(matches.length).toBe(1);
96
+ expect(matches[0].pattern).toBe('async');
97
+ });
98
+ });
99
+
100
+ describe('Adaptive Engine → Skill Injector', () => {
101
+ it('should pass promoted lessons for skill generation', () => {
102
+ // Adaptive engine output
103
+ const promotedLessons = [
104
+ { id: 'SAFE-001', pattern: 'backup-first', severity: 'CRITICAL', promoted: true },
105
+ { id: 'SAFE-002', pattern: 'confirm-delete', severity: 'CRITICAL', promoted: true },
106
+ { id: 'SAFE-003', pattern: 'check-exists', severity: 'CRITICAL', promoted: true }
107
+ ];
108
+
109
+ // Skill injector categorization
110
+ const categories = {};
111
+ for (const lesson of promotedLessons) {
112
+ const category = lesson.id.split('-')[0];
113
+ if (!categories[category]) {
114
+ categories[category] = [];
115
+ }
116
+ categories[category].push(lesson);
117
+ }
118
+
119
+ expect(categories.SAFE.length).toBe(3);
120
+ expect(categories.SAFE.every(l => l.promoted)).toBe(true);
121
+ });
122
+
123
+ it('should generate skill from 3+ lessons', () => {
124
+ const lessons = [
125
+ { id: 'CODE-001', pattern: 'type-annotation' },
126
+ { id: 'CODE-002', pattern: 'null-check' },
127
+ { id: 'CODE-003', pattern: 'error-handling' }
128
+ ];
129
+
130
+ const canGenerateSkill = lessons.length >= 3;
131
+ expect(canGenerateSkill).toBe(true);
132
+
133
+ // Mock skill generation
134
+ const skill = {
135
+ name: 'code-patterns',
136
+ lessonCount: lessons.length,
137
+ generatedAt: new Date().toISOString()
138
+ };
139
+
140
+ expect(skill.name).toBe('code-patterns');
141
+ expect(skill.lessonCount).toBe(3);
142
+ });
143
+ });
144
+
145
+ describe('Success Sensor → Pattern Analyzer', () => {
146
+ it('should pass success format for balance calculation', () => {
147
+ const successes = [
148
+ { pattern: 'async-await', type: 'pattern' },
149
+ { pattern: 'typescript', type: 'pattern' }
150
+ ];
151
+
152
+ const errors = [
153
+ { type: 'test', severity: 'HIGH' }
154
+ ];
155
+
156
+ // Calculate balance
157
+ const totalSuccesses = successes.length;
158
+ const totalFailures = errors.length;
159
+ const total = totalSuccesses + totalFailures;
160
+ const ratio = totalSuccesses / total;
161
+
162
+ expect(ratio).toBeCloseTo(0.67, 1);
163
+ });
164
+ });
165
+
166
+ describe('Rule Sharing → Pre-Execution Check', () => {
167
+ it('should import rules in correct format', () => {
168
+ // Exported rule format
169
+ const exportedRule = {
170
+ id: 'SAFE-001',
171
+ pattern: 'delete-without-backup',
172
+ severity: 'CRITICAL',
173
+ source: 'imported'
174
+ };
175
+
176
+ // Verify format for pre-execution
177
+ expect(exportedRule.id).toBeDefined();
178
+ expect(exportedRule.pattern).toBeDefined();
179
+ expect(exportedRule.severity).toBeDefined();
180
+ expect(['LOW', 'MEDIUM', 'HIGH', 'CRITICAL']).toContain(exportedRule.severity);
181
+ });
182
+ });
183
+
184
+ describe('Full Cross-Script Pipeline', () => {
185
+ it('should maintain data integrity through pipeline', () => {
186
+ // 1. Error sensor detects
187
+ const error = {
188
+ id: 'ERR-001',
189
+ type: 'missing-return-type',
190
+ severity: 'MEDIUM',
191
+ timestamp: new Date().toISOString()
192
+ };
193
+
194
+ // 2. Pattern analyzer creates lesson
195
+ const lesson = {
196
+ id: 'CODE-001',
197
+ pattern: error.type,
198
+ severity: error.severity,
199
+ hitCount: 1
200
+ };
201
+
202
+ // 3. After multiple hits, adaptive engine promotes
203
+ lesson.hitCount = 25;
204
+ if (lesson.hitCount >= 25) {
205
+ lesson.severity = 'CRITICAL';
206
+ lesson.promoted = true;
207
+ }
208
+
209
+ // 4. Pre-execution check uses promoted rule
210
+ const intent = 'create function missing-return-type';
211
+ const matches = intent.includes(lesson.pattern);
212
+
213
+ // 5. Skill injector can use for skill
214
+ const canMakeSkill = lesson.promoted;
215
+
216
+ expect(error.type).toBe(lesson.pattern);
217
+ expect(lesson.severity).toBe('CRITICAL');
218
+ expect(matches).toBe(true);
219
+ expect(canMakeSkill).toBe(true);
220
+ });
221
+ });
222
+ });
@@ -0,0 +1,230 @@
1
+ /**
2
+ * E2E Integration Tests - Full Learning Cycle
3
+ *
4
+ * Tests the complete auto-learn workflow:
5
+ * 1. Error Detection → Lesson Extraction
6
+ * 2. Pattern Analysis → Rule Generation
7
+ * 3. Pre-execution Check → Prevention
8
+ * 4. Adaptive Engine → Severity Adjustment
9
+ */
10
+
11
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
12
+ import fs from 'fs';
13
+ import path from 'path';
14
+
15
+ // Mock knowledge path
16
+ const mockKnowledgePath = path.join(process.cwd(), '.test-knowledge');
17
+
18
+ // Mock data structures
19
+ const createMockError = (id, type, severity = 'HIGH') => ({
20
+ id,
21
+ type,
22
+ severity,
23
+ message: `Test error ${id}`,
24
+ timestamp: new Date().toISOString()
25
+ });
26
+
27
+ const createMockLesson = (id, pattern, severity = 'MEDIUM') => ({
28
+ id,
29
+ pattern,
30
+ severity,
31
+ message: `Lesson from ${pattern}`,
32
+ hitCount: 0
33
+ });
34
+
35
+ describe('E2E: Full Learning Cycle', () => {
36
+ beforeEach(() => {
37
+ // Setup mock knowledge directory
38
+ if (!fs.existsSync(mockKnowledgePath)) {
39
+ fs.mkdirSync(mockKnowledgePath, { recursive: true });
40
+ }
41
+ });
42
+
43
+ afterEach(() => {
44
+ // Cleanup
45
+ if (fs.existsSync(mockKnowledgePath)) {
46
+ fs.rmSync(mockKnowledgePath, { recursive: true });
47
+ }
48
+ });
49
+
50
+ describe('Error → Lesson Flow', () => {
51
+ it('should create lessons from detected errors', () => {
52
+ // Simulate error detection
53
+ const errors = [
54
+ createMockError('ERR-001', 'test-failure'),
55
+ createMockError('ERR-002', 'test-failure'),
56
+ createMockError('ERR-003', 'test-failure')
57
+ ];
58
+
59
+ // Group by type (simulating pattern analysis)
60
+ const errorsByType = {};
61
+ for (const err of errors) {
62
+ errorsByType[err.type] = (errorsByType[err.type] || 0) + 1;
63
+ }
64
+
65
+ // Find high-frequency (3+ occurrences)
66
+ const threshold = 3;
67
+ const highFrequency = Object.entries(errorsByType)
68
+ .filter(([, count]) => count >= threshold);
69
+
70
+ expect(highFrequency.length).toBe(1);
71
+ expect(highFrequency[0][0]).toBe('test-failure');
72
+
73
+ // Generate lesson from high-frequency pattern
74
+ const lesson = createMockLesson(
75
+ 'LEARN-001',
76
+ 'test-failure',
77
+ 'HIGH'
78
+ );
79
+
80
+ expect(lesson.pattern).toBe('test-failure');
81
+ });
82
+ });
83
+
84
+ describe('Lesson → Rule Flow', () => {
85
+ it('should generate rules from lessons with 10+ hits', () => {
86
+ const lesson = createMockLesson('LEARN-001', 'async-await-missing');
87
+ lesson.hitCount = 15; // High hit count
88
+
89
+ const SEVERITY_LEVELS = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
90
+ const currentIndex = SEVERITY_LEVELS.indexOf(lesson.severity);
91
+
92
+ // Adjust severity for 10+ hits
93
+ if (lesson.hitCount >= 10 && currentIndex < SEVERITY_LEVELS.length - 1) {
94
+ lesson.severity = SEVERITY_LEVELS[currentIndex + 1];
95
+ }
96
+
97
+ expect(lesson.severity).toBe('HIGH');
98
+ });
99
+
100
+ it('should maintain lesson through multiple cycles', () => {
101
+ const lesson = createMockLesson('LEARN-001', 'type-error');
102
+
103
+ // Simulate 5 learning cycles
104
+ for (let i = 0; i < 5; i++) {
105
+ lesson.hitCount++;
106
+ lesson.lastHit = new Date().toISOString();
107
+ }
108
+
109
+ expect(lesson.hitCount).toBe(5);
110
+ expect(lesson.lastHit).toBeDefined();
111
+ });
112
+ });
113
+
114
+ describe('Rule → Prevention Flow', () => {
115
+ it('should block execution for matching critical patterns', () => {
116
+ const lessons = [
117
+ { id: 'SAFE-001', pattern: 'delete', severity: 'CRITICAL' }
118
+ ];
119
+
120
+ const intent = 'delete old files';
121
+ const violations = [];
122
+
123
+ for (const lesson of lessons) {
124
+ if (intent.toLowerCase().includes(lesson.pattern.toLowerCase())) {
125
+ if (lesson.severity === 'CRITICAL') {
126
+ violations.push({
127
+ id: lesson.id,
128
+ prevention: `Violation: ${lesson.pattern}`,
129
+ severity: 'CRITICAL'
130
+ });
131
+ }
132
+ }
133
+ }
134
+
135
+ expect(violations.length).toBe(1);
136
+ expect(violations[0].severity).toBe('CRITICAL');
137
+ });
138
+
139
+ it('should allow execution when no violations', () => {
140
+ const lessons = [
141
+ { id: 'SAFE-001', pattern: 'delete', severity: 'CRITICAL' }
142
+ ];
143
+
144
+ const intent = 'create new component';
145
+ const violations = [];
146
+
147
+ for (const lesson of lessons) {
148
+ if (intent.toLowerCase().includes(lesson.pattern.toLowerCase())) {
149
+ violations.push(lesson);
150
+ }
151
+ }
152
+
153
+ expect(violations.length).toBe(0);
154
+ });
155
+ });
156
+
157
+ describe('Adaptive Cycle', () => {
158
+ it('should promote rules with 95%+ accuracy', () => {
159
+ const lesson = createMockLesson('LEARN-001', 'typescript-strict');
160
+ lesson.hitCount = 1;
161
+ lesson.successCount = 19; // 95% accuracy
162
+
163
+ const totalAttempts = lesson.hitCount + lesson.successCount;
164
+ const accuracy = lesson.successCount / totalAttempts;
165
+
166
+ if (accuracy >= 0.95 && totalAttempts >= 10) {
167
+ lesson.promoted = true;
168
+ lesson.severity = 'CRITICAL';
169
+ }
170
+
171
+ expect(lesson.promoted).toBe(true);
172
+ expect(lesson.severity).toBe('CRITICAL');
173
+ expect(accuracy).toBeGreaterThanOrEqual(0.95);
174
+ });
175
+
176
+ it('should demote rules with <50% accuracy', () => {
177
+ const lesson = createMockLesson('LEARN-001', 'outdated-pattern');
178
+ lesson.severity = 'HIGH';
179
+ lesson.hitCount = 8;
180
+ lesson.successCount = 2; // 20% accuracy
181
+
182
+ const totalAttempts = lesson.hitCount + lesson.successCount;
183
+ const accuracy = lesson.successCount / totalAttempts;
184
+
185
+ if (accuracy < 0.50 && totalAttempts >= 10) {
186
+ lesson.demoted = true;
187
+ lesson.severity = 'LOW';
188
+ }
189
+
190
+ expect(lesson.demoted).toBe(true);
191
+ expect(lesson.severity).toBe('LOW');
192
+ });
193
+ });
194
+
195
+ describe('Complete Workflow', () => {
196
+ it('should handle full cycle: detect → learn → prevent', () => {
197
+ // Step 1: Detect errors
198
+ const detectedErrors = [
199
+ createMockError('E1', 'missing-await'),
200
+ createMockError('E2', 'missing-await'),
201
+ createMockError('E3', 'missing-await')
202
+ ];
203
+
204
+ // Step 2: Analyze patterns
205
+ const patterns = {};
206
+ for (const err of detectedErrors) {
207
+ patterns[err.type] = (patterns[err.type] || 0) + 1;
208
+ }
209
+
210
+ // Step 3: Generate lesson from high-frequency
211
+ const highFreq = Object.entries(patterns)
212
+ .filter(([, c]) => c >= 3);
213
+
214
+ expect(highFreq.length).toBeGreaterThan(0);
215
+
216
+ const lessons = highFreq.map(([type]) =>
217
+ createMockLesson(`LEARN-${type}`, type, 'HIGH')
218
+ );
219
+
220
+ // Step 4: Check new intent against lessons
221
+ const newIntent = 'create function with missing await';
222
+ const matches = lessons.filter(l =>
223
+ newIntent.toLowerCase().includes(l.pattern)
224
+ );
225
+
226
+ expect(matches.length).toBe(1);
227
+ expect(matches[0].severity).toBe('HIGH');
228
+ });
229
+ });
230
+ });