add-skill-kit 3.2.3 → 3.2.5

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 (126) hide show
  1. package/README.md +1 -1
  2. package/bin/lib/commands/help.js +0 -4
  3. package/bin/lib/commands/install.js +90 -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/agent.js +191 -0
  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 +154 -0
  15. package/lib/agent-cli/lib/audit.test.js +100 -0
  16. package/lib/agent-cli/lib/auto-learn.js +319 -0
  17. package/lib/agent-cli/lib/auto_preview.py +148 -0
  18. package/lib/agent-cli/lib/backup.js +138 -0
  19. package/lib/agent-cli/lib/backup.test.js +78 -0
  20. package/lib/agent-cli/lib/checklist.py +222 -0
  21. package/lib/agent-cli/lib/cognitive-lesson.js +476 -0
  22. package/lib/agent-cli/lib/completion.js +149 -0
  23. package/lib/agent-cli/lib/config.js +35 -0
  24. package/lib/agent-cli/lib/eslint-fix.js +238 -0
  25. package/lib/agent-cli/lib/evolution-signal.js +215 -0
  26. package/lib/agent-cli/lib/export.js +86 -0
  27. package/lib/agent-cli/lib/export.test.js +65 -0
  28. package/lib/agent-cli/lib/fix.js +337 -0
  29. package/lib/agent-cli/lib/fix.test.js +80 -0
  30. package/lib/agent-cli/lib/gemini-export.js +83 -0
  31. package/lib/agent-cli/lib/generate-registry.js +42 -0
  32. package/lib/agent-cli/lib/hooks/install-hooks.js +152 -0
  33. package/lib/agent-cli/lib/hooks/lint-learn.js +172 -0
  34. package/lib/agent-cli/lib/ignore.js +116 -0
  35. package/lib/agent-cli/lib/ignore.test.js +58 -0
  36. package/lib/agent-cli/lib/init.js +124 -0
  37. package/lib/agent-cli/lib/learn.js +255 -0
  38. package/lib/agent-cli/lib/learn.test.js +70 -0
  39. package/lib/agent-cli/lib/migrate-to-v4.js +322 -0
  40. package/lib/agent-cli/lib/proposals.js +199 -0
  41. package/lib/agent-cli/lib/proposals.test.js +56 -0
  42. package/lib/agent-cli/lib/recall.js +820 -0
  43. package/lib/agent-cli/lib/recall.test.js +107 -0
  44. package/lib/agent-cli/lib/selfevolution-bridge.js +167 -0
  45. package/lib/agent-cli/lib/session_manager.py +120 -0
  46. package/lib/agent-cli/lib/settings.js +227 -0
  47. package/lib/agent-cli/lib/skill-learn.js +296 -0
  48. package/lib/agent-cli/lib/stats.js +132 -0
  49. package/lib/agent-cli/lib/stats.test.js +94 -0
  50. package/lib/agent-cli/lib/types.js +33 -0
  51. package/lib/agent-cli/lib/ui/audit-ui.js +146 -0
  52. package/lib/agent-cli/lib/ui/backup-ui.js +107 -0
  53. package/lib/agent-cli/lib/ui/clack-helpers.js +317 -0
  54. package/lib/agent-cli/lib/ui/common.js +83 -0
  55. package/lib/agent-cli/lib/ui/completion-ui.js +126 -0
  56. package/lib/agent-cli/lib/ui/custom-select.js +69 -0
  57. package/lib/agent-cli/lib/ui/dashboard-ui.js +222 -0
  58. package/lib/agent-cli/lib/ui/evolution-signals-ui.js +107 -0
  59. package/lib/agent-cli/lib/ui/export-ui.js +94 -0
  60. package/lib/agent-cli/lib/ui/fix-all-ui.js +191 -0
  61. package/lib/agent-cli/lib/ui/help-ui.js +49 -0
  62. package/lib/agent-cli/lib/ui/index.js +199 -0
  63. package/lib/agent-cli/lib/ui/init-ui.js +56 -0
  64. package/lib/agent-cli/lib/ui/knowledge-ui.js +55 -0
  65. package/lib/agent-cli/lib/ui/learn-ui.js +706 -0
  66. package/lib/agent-cli/lib/ui/lessons-ui.js +148 -0
  67. package/lib/agent-cli/lib/ui/pretty.js +145 -0
  68. package/lib/agent-cli/lib/ui/proposals-ui.js +99 -0
  69. package/lib/agent-cli/lib/ui/recall-ui.js +342 -0
  70. package/lib/agent-cli/lib/ui/routing-demo.js +79 -0
  71. package/lib/agent-cli/lib/ui/routing-ui.js +325 -0
  72. package/lib/agent-cli/lib/ui/settings-ui.js +381 -0
  73. package/lib/agent-cli/lib/ui/stats-ui.js +123 -0
  74. package/lib/agent-cli/lib/ui/watch-ui.js +236 -0
  75. package/lib/agent-cli/lib/verify_all.py +327 -0
  76. package/lib/agent-cli/lib/watcher.js +181 -0
  77. package/lib/agent-cli/lib/watcher.test.js +85 -0
  78. package/lib/agent-cli/package.json +51 -0
  79. package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
  80. package/lib/agent-cli/scripts/dashboard_server.js +224 -0
  81. package/lib/agent-cli/scripts/error_sensor.js +565 -0
  82. package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
  83. package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
  84. package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
  85. package/lib/agent-cli/scripts/rule_sharing.js +374 -0
  86. package/lib/agent-cli/scripts/skill_injector.js +387 -0
  87. package/lib/agent-cli/scripts/success_sensor.js +500 -0
  88. package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
  89. package/lib/agent-cli/services/auto-learn-service.js +247 -0
  90. package/lib/agent-cli/src/MIGRATION.md +418 -0
  91. package/lib/agent-cli/src/README.md +367 -0
  92. package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
  93. package/lib/agent-cli/src/core/evolution/index.js +17 -0
  94. package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
  95. package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
  96. package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
  97. package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
  98. package/lib/agent-cli/src/core/index.js +15 -0
  99. package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
  100. package/lib/agent-cli/src/core/learning/index.js +12 -0
  101. package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
  102. package/lib/agent-cli/src/core/scanning/index.js +14 -0
  103. package/lib/agent-cli/src/data/index.js +13 -0
  104. package/lib/agent-cli/src/data/repositories/index.js +8 -0
  105. package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
  106. package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
  107. package/lib/agent-cli/src/data/storage/index.js +8 -0
  108. package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
  109. package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
  110. package/lib/agent-cli/src/infrastructure/index.js +13 -0
  111. package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
  112. package/lib/agent-cli/src/services/export-service.js +162 -0
  113. package/lib/agent-cli/src/services/index.js +13 -0
  114. package/lib/agent-cli/src/services/learning-service.js +99 -0
  115. package/lib/agent-cli/types/index.d.ts +343 -0
  116. package/lib/agent-cli/utils/benchmark.js +269 -0
  117. package/lib/agent-cli/utils/logger.js +303 -0
  118. package/lib/agent-cli/utils/ml_patterns.js +300 -0
  119. package/lib/agent-cli/utils/recovery.js +312 -0
  120. package/lib/agent-cli/utils/telemetry.js +290 -0
  121. package/lib/agentskillskit-cli/README.md +21 -0
  122. package/{node_modules/agentskillskit-cli/bin → lib/agentskillskit-cli}/ag-smart.js +15 -15
  123. package/lib/agentskillskit-cli/package.json +51 -0
  124. package/package.json +19 -9
  125. /package/bin/{cli.js → kit.js} +0 -0
  126. /package/{node_modules/agentskillskit-cli → lib/agent-cli}/README.md +0 -0
@@ -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
+ });
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Unit Tests for pattern_analyzer.js
3
+ *
4
+ * Tests core pattern analysis functions:
5
+ * - analyzeErrorPatterns
6
+ * - analyzeCorrectionPatterns
7
+ * - analyzeSuccessPatterns
8
+ * - calculateSuccessFailureRatio
9
+ * - calculateTrends
10
+ * - findHighFrequencyPatterns
11
+ */
12
+
13
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
14
+ import path from 'path';
15
+ import { fileURLToPath } from 'url';
16
+
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+
20
+ // Mock fs module
21
+ vi.mock('fs', async () => {
22
+ const actual = await vi.importActual('fs');
23
+ return {
24
+ ...actual,
25
+ default: {
26
+ ...actual,
27
+ existsSync: vi.fn(() => true),
28
+ readFileSync: vi.fn(),
29
+ writeFileSync: vi.fn(),
30
+ mkdirSync: vi.fn()
31
+ },
32
+ existsSync: vi.fn(() => true),
33
+ readFileSync: vi.fn(),
34
+ writeFileSync: vi.fn(),
35
+ mkdirSync: vi.fn()
36
+ };
37
+ });
38
+
39
+ // Test fixtures
40
+ const mockErrors = [
41
+ { id: 'ERR-1', type: 'test', severity: 'HIGH', timestamp: new Date().toISOString() },
42
+ { id: 'ERR-2', type: 'test', severity: 'HIGH', timestamp: new Date().toISOString() },
43
+ { id: 'ERR-3', type: 'build', severity: 'CRITICAL', timestamp: new Date().toISOString() }
44
+ ];
45
+
46
+ const mockCorrections = [
47
+ { category: 'import-fix', timestamp: new Date().toISOString() },
48
+ { category: 'import-fix', timestamp: new Date().toISOString() },
49
+ { category: 'type-fix', timestamp: new Date().toISOString() }
50
+ ];
51
+
52
+ const mockSuccesses = [
53
+ { pattern: 'async-await', type: 'pattern', detectedAt: new Date().toISOString() },
54
+ { pattern: 'typescript-types', type: 'pattern', detectedAt: new Date().toISOString() },
55
+ { pattern: 'async-await', type: 'pattern', detectedAt: new Date().toISOString() }
56
+ ];
57
+
58
+ describe('Pattern Analyzer', () => {
59
+ describe('analyzeErrorPatterns', () => {
60
+ it('should group errors by type', () => {
61
+ const patterns = {
62
+ byType: {},
63
+ bySeverity: {},
64
+ byFile: {},
65
+ byTime: {},
66
+ total: mockErrors.length
67
+ };
68
+
69
+ for (const err of mockErrors) {
70
+ patterns.byType[err.type] = (patterns.byType[err.type] || 0) + 1;
71
+ patterns.bySeverity[err.severity] = (patterns.bySeverity[err.severity] || 0) + 1;
72
+ }
73
+
74
+ expect(patterns.byType.test).toBe(2);
75
+ expect(patterns.byType.build).toBe(1);
76
+ expect(patterns.bySeverity.HIGH).toBe(2);
77
+ expect(patterns.bySeverity.CRITICAL).toBe(1);
78
+ });
79
+ });
80
+
81
+ describe('analyzeCorrectionPatterns', () => {
82
+ it('should group corrections by category', () => {
83
+ const patterns = { byCategory: {} };
84
+
85
+ for (const corr of mockCorrections) {
86
+ patterns.byCategory[corr.category] = (patterns.byCategory[corr.category] || 0) + 1;
87
+ }
88
+
89
+ expect(patterns.byCategory['import-fix']).toBe(2);
90
+ expect(patterns.byCategory['type-fix']).toBe(1);
91
+ });
92
+ });
93
+
94
+ describe('analyzeSuccessPatterns', () => {
95
+ it('should group successes by pattern', () => {
96
+ const patterns = { byPattern: {} };
97
+
98
+ for (const s of mockSuccesses) {
99
+ patterns.byPattern[s.pattern] = (patterns.byPattern[s.pattern] || 0) + 1;
100
+ }
101
+
102
+ expect(patterns.byPattern['async-await']).toBe(2);
103
+ expect(patterns.byPattern['typescript-types']).toBe(1);
104
+ });
105
+ });
106
+
107
+ describe('calculateSuccessFailureRatio', () => {
108
+ it('should return EXCELLENT for 70%+ success rate', () => {
109
+ const errors = [{ id: 1 }];
110
+ const corrections = [{ id: 1 }];
111
+ const successes = Array(8).fill({ id: 1 }); // 8 successes, 2 failures = 80%
112
+
113
+ const totalFailures = errors.length + corrections.length;
114
+ const totalSuccesses = successes.length;
115
+ const total = totalFailures + totalSuccesses;
116
+ const successRate = totalSuccesses / total;
117
+
118
+ expect(successRate).toBeGreaterThan(0.7);
119
+ });
120
+
121
+ it('should return NEEDS_ATTENTION for <30% success rate', () => {
122
+ const errors = Array(7).fill({ id: 1 });
123
+ const corrections = [];
124
+ const successes = [{ id: 1 }]; // 1 success, 7 failures = ~12%
125
+
126
+ const totalFailures = errors.length + corrections.length;
127
+ const totalSuccesses = successes.length;
128
+ const total = totalFailures + totalSuccesses;
129
+ const successRate = totalSuccesses / total;
130
+
131
+ expect(successRate).toBeLessThan(0.3);
132
+ });
133
+
134
+ it('should handle empty data', () => {
135
+ const total = 0;
136
+ expect(total).toBe(0);
137
+ });
138
+ });
139
+
140
+ describe('calculateTrends', () => {
141
+ it('should calculate weekly comparison', () => {
142
+ const now = new Date();
143
+ const oneWeekAgo = new Date(now - 7 * 24 * 60 * 60 * 1000);
144
+ const twoWeeksAgo = new Date(now - 14 * 24 * 60 * 60 * 1000);
145
+
146
+ const thisWeekErrors = mockErrors.filter(e => new Date(e.timestamp) > oneWeekAgo);
147
+ const lastWeekErrors = mockErrors.filter(e => {
148
+ const d = new Date(e.timestamp);
149
+ return d > twoWeeksAgo && d <= oneWeekAgo;
150
+ });
151
+
152
+ expect(thisWeekErrors.length).toBeGreaterThanOrEqual(0);
153
+ expect(lastWeekErrors.length).toBeGreaterThanOrEqual(0);
154
+ });
155
+ });
156
+
157
+ describe('findHighFrequencyPatterns', () => {
158
+ it('should find patterns occurring 3+ times', () => {
159
+ const threshold = 3;
160
+ const errorPatterns = {
161
+ byType: { 'test': 5, 'build': 2, 'lint': 4 }
162
+ };
163
+
164
+ const highFrequency = Object.entries(errorPatterns.byType)
165
+ .filter(([, count]) => count >= threshold)
166
+ .map(([type, count]) => ({ type, count }));
167
+
168
+ expect(highFrequency.length).toBe(2);
169
+ expect(highFrequency.find(p => p.type === 'test')?.count).toBe(5);
170
+ expect(highFrequency.find(p => p.type === 'lint')?.count).toBe(4);
171
+ });
172
+ });
173
+ });
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Unit Tests for pre_execution_check.js
3
+ *
4
+ * Tests:
5
+ * - detectIntent
6
+ * - checkIntent
7
+ * - generateAdvice
8
+ */
9
+
10
+ import { describe, it, expect, vi } from 'vitest';
11
+
12
+ // Mock fs module
13
+ vi.mock('fs', async () => {
14
+ const actual = await vi.importActual('fs');
15
+ return {
16
+ ...actual,
17
+ default: {
18
+ ...actual,
19
+ existsSync: vi.fn(() => false),
20
+ readFileSync: vi.fn(() => '{}'),
21
+ writeFileSync: vi.fn()
22
+ },
23
+ existsSync: vi.fn(() => false),
24
+ readFileSync: vi.fn(() => '{}')
25
+ };
26
+ });
27
+
28
+ describe('Pre-Execution Check', () => {
29
+ describe('detectIntent', () => {
30
+ it('should detect delete operations as CRITICAL', () => {
31
+ const input = 'delete file test.js';
32
+ const intents = [];
33
+
34
+ if (/\b(delete|remove|unlink|rm)\b/i.test(input)) {
35
+ intents.push({ action: 'delete', target: 'file', risk: 'CRITICAL' });
36
+ }
37
+
38
+ expect(intents.length).toBe(1);
39
+ expect(intents[0].risk).toBe('CRITICAL');
40
+ });
41
+
42
+ it('should detect rename operations as HIGH risk', () => {
43
+ const input = 'rename component to new name';
44
+ const intents = [];
45
+
46
+ if (/\b(rename|mv|move)\b/i.test(input)) {
47
+ intents.push({ action: 'rename', target: 'file', risk: 'HIGH' });
48
+ }
49
+
50
+ expect(intents.length).toBe(1);
51
+ expect(intents[0].risk).toBe('HIGH');
52
+ });
53
+
54
+ it('should detect create operations as LOW risk', () => {
55
+ const input = 'create new component file';
56
+ const intents = [];
57
+
58
+ if (/\b(create|new|add|write)\b.*\b(file|component|module)\b/i.test(input)) {
59
+ intents.push({ action: 'create', target: 'file', risk: 'LOW' });
60
+ }
61
+
62
+ expect(intents.length).toBe(1);
63
+ expect(intents[0].risk).toBe('LOW');
64
+ });
65
+
66
+ it('should detect async operations', () => {
67
+ const input = 'create async function to fetch API';
68
+ const intents = [];
69
+
70
+ if (/\b(async|await|promise|fetch)\b/i.test(input)) {
71
+ intents.push({ action: 'async', target: 'code', risk: 'MEDIUM' });
72
+ }
73
+
74
+ expect(intents.length).toBe(1);
75
+ expect(intents[0].action).toBe('async');
76
+ });
77
+
78
+ it('should detect database operations as HIGH risk', () => {
79
+ const input = 'query database for users';
80
+ const intents = [];
81
+
82
+ if (/\b(database|db|query|sql|prisma)\b/i.test(input)) {
83
+ intents.push({ action: 'database', target: 'data', risk: 'HIGH' });
84
+ }
85
+
86
+ expect(intents.length).toBe(1);
87
+ expect(intents[0].risk).toBe('HIGH');
88
+ });
89
+
90
+ it('should detect completion as CRITICAL', () => {
91
+ const input = 'complete task and notify user';
92
+ const intents = [];
93
+
94
+ if (/\b(complete|finish|done|notify|submit)\b/i.test(input)) {
95
+ intents.push({ action: 'complete', target: 'task', risk: 'CRITICAL' });
96
+ }
97
+
98
+ expect(intents.length).toBe(1);
99
+ expect(intents[0].risk).toBe('CRITICAL');
100
+ });
101
+ });
102
+
103
+ describe('checkIntent', () => {
104
+ it('should return violations for CRITICAL intents', () => {
105
+ const violations = [];
106
+ const intent = 'delete all files';
107
+
108
+ if (/\b(delete)\b/i.test(intent)) {
109
+ violations.push({
110
+ id: 'INTENT-DELETE',
111
+ severity: 'CRITICAL',
112
+ source: 'intent-detection'
113
+ });
114
+ }
115
+
116
+ expect(violations.some(v => v.severity === 'CRITICAL')).toBe(true);
117
+ });
118
+
119
+ it('should match builtin rules', () => {
120
+ const BUILTIN_RULES = [
121
+ {
122
+ id: 'BUILTIN-001',
123
+ name: 'TypeScript type safety',
124
+ check: (intent) => intent.toLowerCase().includes('function'),
125
+ severity: 'HIGH'
126
+ }
127
+ ];
128
+
129
+ const intent = 'create function handler';
130
+ const warnings = [];
131
+
132
+ for (const rule of BUILTIN_RULES) {
133
+ if (rule.check(intent)) {
134
+ warnings.push({ id: rule.id, severity: rule.severity });
135
+ }
136
+ }
137
+
138
+ expect(warnings.length).toBe(1);
139
+ expect(warnings[0].id).toBe('BUILTIN-001');
140
+ });
141
+ });
142
+
143
+ describe('generateAdvice', () => {
144
+ it('should prioritize CRITICAL first', () => {
145
+ const all = [
146
+ { severity: 'MEDIUM', prevention: 'Medium advice' },
147
+ { severity: 'CRITICAL', prevention: 'Critical advice' },
148
+ { severity: 'HIGH', prevention: 'High advice' }
149
+ ];
150
+
151
+ const critical = all.filter(v => v.severity === 'CRITICAL');
152
+ const high = all.filter(v => v.severity === 'HIGH');
153
+ const other = all.filter(v => v.severity !== 'CRITICAL' && v.severity !== 'HIGH');
154
+
155
+ expect(critical.length).toBe(1);
156
+ expect(high.length).toBe(1);
157
+ expect(other.length).toBe(1);
158
+ });
159
+
160
+ it('should block execution for CRITICAL violations', () => {
161
+ const violations = [{ severity: 'CRITICAL' }];
162
+ const hasCritical = violations.some(v => v.severity === 'CRITICAL');
163
+
164
+ expect(hasCritical).toBe(true);
165
+ });
166
+ });
167
+ });