pikakit 1.0.0
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 +239 -0
- package/bin/add-skill-kit.js +3 -0
- package/bin/cli.mjs +6 -0
- package/bin/kit.mjs +89 -0
- package/bin/lib/agents.js +208 -0
- package/bin/lib/commands/analyze.js +70 -0
- package/bin/lib/commands/cache.js +65 -0
- package/bin/lib/commands/doctor.js +75 -0
- package/bin/lib/commands/help.js +155 -0
- package/bin/lib/commands/info.js +38 -0
- package/bin/lib/commands/init.js +39 -0
- package/bin/lib/commands/install.js +803 -0
- package/bin/lib/commands/list.js +43 -0
- package/bin/lib/commands/lock.js +57 -0
- package/bin/lib/commands/uninstall.js +307 -0
- package/bin/lib/commands/update.js +55 -0
- package/bin/lib/commands/validate.js +69 -0
- package/bin/lib/commands/verify.js +56 -0
- package/bin/lib/config.js +81 -0
- package/bin/lib/helpers.js +196 -0
- package/bin/lib/helpers.test.js +60 -0
- package/bin/lib/installer.js +164 -0
- package/bin/lib/skills.js +119 -0
- package/bin/lib/skills.test.js +109 -0
- package/bin/lib/types.js +82 -0
- package/bin/lib/ui.js +329 -0
- package/lib/agent-cli/README.md +21 -0
- 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/agent.js +191 -0
- 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 +154 -0
- package/lib/agent-cli/lib/audit.test.js +100 -0
- package/lib/agent-cli/lib/auto-learn.js +319 -0
- package/lib/agent-cli/lib/backup.js +138 -0
- package/lib/agent-cli/lib/backup.test.js +78 -0
- package/lib/agent-cli/lib/cognitive-lesson.js +476 -0
- package/lib/agent-cli/lib/completion.js +149 -0
- package/lib/agent-cli/lib/config.js +35 -0
- package/lib/agent-cli/lib/eslint-fix.js +238 -0
- package/lib/agent-cli/lib/evolution-signal.js +215 -0
- package/lib/agent-cli/lib/export.js +86 -0
- package/lib/agent-cli/lib/export.test.js +65 -0
- package/lib/agent-cli/lib/fix.js +337 -0
- package/lib/agent-cli/lib/fix.test.js +80 -0
- package/lib/agent-cli/lib/gemini-export.js +83 -0
- package/lib/agent-cli/lib/generate-registry.js +42 -0
- package/lib/agent-cli/lib/hooks/install-hooks.js +152 -0
- package/lib/agent-cli/lib/hooks/lint-learn.js +172 -0
- package/lib/agent-cli/lib/ignore.js +116 -0
- package/lib/agent-cli/lib/ignore.test.js +58 -0
- package/lib/agent-cli/lib/init.js +124 -0
- package/lib/agent-cli/lib/learn.js +255 -0
- package/lib/agent-cli/lib/learn.test.js +70 -0
- package/lib/agent-cli/lib/migrate-to-v4.js +322 -0
- package/lib/agent-cli/lib/proposals.js +199 -0
- package/lib/agent-cli/lib/proposals.test.js +56 -0
- package/lib/agent-cli/lib/recall.js +820 -0
- package/lib/agent-cli/lib/recall.test.js +107 -0
- package/lib/agent-cli/lib/selfevolution-bridge.js +167 -0
- package/lib/agent-cli/lib/settings.js +227 -0
- package/lib/agent-cli/lib/skill-learn.js +296 -0
- package/lib/agent-cli/lib/stats.js +132 -0
- package/lib/agent-cli/lib/stats.test.js +94 -0
- package/lib/agent-cli/lib/types.js +33 -0
- package/lib/agent-cli/lib/ui/audit-ui.js +146 -0
- package/lib/agent-cli/lib/ui/backup-ui.js +107 -0
- package/lib/agent-cli/lib/ui/clack-helpers.js +317 -0
- package/lib/agent-cli/lib/ui/common.js +83 -0
- package/lib/agent-cli/lib/ui/completion-ui.js +126 -0
- package/lib/agent-cli/lib/ui/custom-select.js +69 -0
- package/lib/agent-cli/lib/ui/dashboard-ui.js +222 -0
- package/lib/agent-cli/lib/ui/evolution-signals-ui.js +107 -0
- package/lib/agent-cli/lib/ui/export-ui.js +94 -0
- package/lib/agent-cli/lib/ui/fix-all-ui.js +191 -0
- package/lib/agent-cli/lib/ui/help-ui.js +49 -0
- package/lib/agent-cli/lib/ui/index.js +199 -0
- package/lib/agent-cli/lib/ui/init-ui.js +56 -0
- package/lib/agent-cli/lib/ui/knowledge-ui.js +55 -0
- package/lib/agent-cli/lib/ui/learn-ui.js +706 -0
- package/lib/agent-cli/lib/ui/lessons-ui.js +148 -0
- package/lib/agent-cli/lib/ui/pretty.js +145 -0
- package/lib/agent-cli/lib/ui/proposals-ui.js +99 -0
- package/lib/agent-cli/lib/ui/recall-ui.js +342 -0
- package/lib/agent-cli/lib/ui/routing-demo.js +79 -0
- package/lib/agent-cli/lib/ui/routing-ui.js +325 -0
- package/lib/agent-cli/lib/ui/settings-ui.js +381 -0
- package/lib/agent-cli/lib/ui/stats-ui.js +123 -0
- package/lib/agent-cli/lib/ui/watch-ui.js +236 -0
- package/lib/agent-cli/lib/watcher.js +181 -0
- package/lib/agent-cli/lib/watcher.test.js +85 -0
- package/lib/agent-cli/package.json +51 -0
- 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/README.md +21 -0
- package/lib/agentskillskit-cli/ag-smart.js +158 -0
- package/lib/agentskillskit-cli/package.json +51 -0
- package/package.json +79 -0
- package/specs/ADD_SKILL_SPEC.md +333 -0
- package/specs/REGISTRY_V2_SPEC.md +334 -0
|
@@ -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
|
+
});
|
|
@@ -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
|
+
});
|