add-skill-kit 3.2.4 → 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.
- package/README.md +1 -1
- package/bin/lib/commands/help.js +0 -4
- package/bin/lib/commands/install.js +90 -9
- package/bin/lib/ui.js +1 -1
- package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
- package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
- package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
- package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
- package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
- package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
- package/lib/agent-cli/bin/{ag-smart.js → agent.js} +48 -15
- package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
- package/lib/agent-cli/dashboard/index.html +538 -0
- package/lib/agent-cli/lib/audit.js +2 -2
- package/lib/agent-cli/lib/auto-learn.js +8 -8
- package/lib/agent-cli/lib/eslint-fix.js +1 -1
- package/lib/agent-cli/lib/fix.js +5 -5
- package/lib/agent-cli/lib/hooks/install-hooks.js +4 -4
- package/lib/agent-cli/lib/hooks/lint-learn.js +4 -4
- package/lib/agent-cli/lib/learn.js +10 -10
- package/lib/agent-cli/lib/recall.js +1 -1
- package/lib/agent-cli/lib/settings.js +24 -0
- package/lib/agent-cli/lib/skill-learn.js +2 -2
- package/lib/agent-cli/lib/stats.js +3 -3
- package/lib/agent-cli/lib/ui/dashboard-ui.js +103 -4
- package/lib/agent-cli/lib/ui/index.js +36 -6
- package/lib/agent-cli/lib/watcher.js +2 -2
- package/lib/agent-cli/package.json +4 -4
- package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
- package/lib/agent-cli/scripts/dashboard_server.js +224 -0
- package/lib/agent-cli/scripts/error_sensor.js +565 -0
- package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
- package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
- package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
- package/lib/agent-cli/scripts/rule_sharing.js +374 -0
- package/lib/agent-cli/scripts/skill_injector.js +387 -0
- package/lib/agent-cli/scripts/success_sensor.js +500 -0
- package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
- package/lib/agent-cli/services/auto-learn-service.js +247 -0
- package/lib/agent-cli/src/MIGRATION.md +418 -0
- package/lib/agent-cli/src/README.md +367 -0
- package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
- package/lib/agent-cli/src/core/evolution/index.js +17 -0
- package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
- package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
- package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
- package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
- package/lib/agent-cli/src/core/index.js +15 -0
- package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
- package/lib/agent-cli/src/core/learning/index.js +12 -0
- package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
- package/lib/agent-cli/src/core/scanning/index.js +14 -0
- package/lib/agent-cli/src/data/index.js +13 -0
- package/lib/agent-cli/src/data/repositories/index.js +8 -0
- package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
- package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
- package/lib/agent-cli/src/data/storage/index.js +8 -0
- package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
- package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
- package/lib/agent-cli/src/infrastructure/index.js +13 -0
- package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
- package/lib/agent-cli/src/services/export-service.js +162 -0
- package/lib/agent-cli/src/services/index.js +13 -0
- package/lib/agent-cli/src/services/learning-service.js +99 -0
- package/lib/agent-cli/types/index.d.ts +343 -0
- package/lib/agent-cli/utils/benchmark.js +269 -0
- package/lib/agent-cli/utils/logger.js +303 -0
- package/lib/agent-cli/utils/ml_patterns.js +300 -0
- package/lib/agent-cli/utils/recovery.js +312 -0
- package/lib/agent-cli/utils/telemetry.js +290 -0
- package/lib/agentskillskit-cli/ag-smart.js +15 -15
- package/lib/agentskillskit-cli/package.json +3 -3
- package/package.json +11 -5
- /package/bin/{cli.js → kit.js} +0 -0
package/README.md
CHANGED
package/bin/lib/commands/help.js
CHANGED
|
@@ -12,10 +12,6 @@ export async function run() {
|
|
|
12
12
|
let running = true;
|
|
13
13
|
|
|
14
14
|
while (running) {
|
|
15
|
-
stepLine();
|
|
16
|
-
step(c.bold("kit") + c.dim(` v${VERSION}`));
|
|
17
|
-
step(c.dim("What would you like to know?"));
|
|
18
|
-
stepLine();
|
|
19
15
|
|
|
20
16
|
const choice = await select({
|
|
21
17
|
message: "Select a topic",
|
|
@@ -630,17 +630,37 @@ export async function run(spec) {
|
|
|
630
630
|
|
|
631
631
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
632
632
|
|
|
633
|
+
// Ask user about AutoLearn installation
|
|
634
|
+
stepLine();
|
|
635
|
+
const installAutoLearn = await confirm({
|
|
636
|
+
message: "Install AutoLearn (enables 'agent' command for learning & self-improvement)?",
|
|
637
|
+
initialValue: true
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
if (isCancel(installAutoLearn)) {
|
|
641
|
+
cancel("Installation cancelled");
|
|
642
|
+
process.exit(0);
|
|
643
|
+
}
|
|
644
|
+
|
|
633
645
|
// Install CLI package
|
|
634
646
|
stepLine();
|
|
635
647
|
const cliSpinner = spinner();
|
|
636
|
-
const cliPackage = "
|
|
648
|
+
const cliPackage = "add-skill-kit";
|
|
637
649
|
|
|
638
650
|
if (isGlobal) {
|
|
639
|
-
|
|
651
|
+
if (installAutoLearn) {
|
|
652
|
+
cliSpinner.start(`Installing CLI globally (${cliPackage})`);
|
|
653
|
+
} else {
|
|
654
|
+
cliSpinner.start(`Installing kit CLI globally (${cliPackage})`);
|
|
655
|
+
}
|
|
640
656
|
try {
|
|
641
657
|
await execAsync(`npm install -g ${cliPackage}`, { timeout: 120000 });
|
|
642
|
-
cliSpinner.stop("
|
|
643
|
-
|
|
658
|
+
cliSpinner.stop("CLI installed globally");
|
|
659
|
+
if (installAutoLearn) {
|
|
660
|
+
step(c.dim("Commands: agent, kit"));
|
|
661
|
+
} else {
|
|
662
|
+
step(c.dim("Command: kit"));
|
|
663
|
+
}
|
|
644
664
|
} catch (e) {
|
|
645
665
|
cliSpinner.stop(c.yellow("Global CLI installation failed"));
|
|
646
666
|
step(c.dim(`Try running manually: npm i -g ${cliPackage}`));
|
|
@@ -649,7 +669,7 @@ export async function run(spec) {
|
|
|
649
669
|
cliSpinner.start(`Installing Agent CLI locally (${cliPackage})`);
|
|
650
670
|
try {
|
|
651
671
|
await execAsync(`npm install -D ${cliPackage}`, { timeout: 120000 });
|
|
652
|
-
cliSpinner.stop("
|
|
672
|
+
cliSpinner.stop("CLI installed locally");
|
|
653
673
|
|
|
654
674
|
// Add npm scripts to package.json
|
|
655
675
|
try {
|
|
@@ -658,25 +678,86 @@ export async function run(spec) {
|
|
|
658
678
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
659
679
|
pkg.scripts = pkg.scripts || {};
|
|
660
680
|
|
|
661
|
-
//
|
|
662
|
-
if (!pkg.scripts.
|
|
681
|
+
// Always add kit script
|
|
682
|
+
if (!pkg.scripts.kit) {
|
|
683
|
+
pkg.scripts.kit = "kit";
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Add agent script only if AutoLearn enabled
|
|
687
|
+
if (installAutoLearn && !pkg.scripts.agent) {
|
|
663
688
|
pkg.scripts.agent = "agent";
|
|
664
689
|
}
|
|
665
690
|
|
|
666
691
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
667
|
-
|
|
692
|
+
if (installAutoLearn) {
|
|
693
|
+
step(c.green("✓ Added npm scripts: 'agent', 'kit'"));
|
|
694
|
+
} else {
|
|
695
|
+
step(c.green("✓ Added npm script: 'kit'"));
|
|
696
|
+
}
|
|
668
697
|
}
|
|
669
698
|
} catch (scriptErr) {
|
|
670
699
|
step(c.yellow("⚠ Could not add npm scripts automatically"));
|
|
671
700
|
}
|
|
672
701
|
|
|
673
|
-
|
|
702
|
+
// Create wrapper scripts for direct command access (Windows + Unix)
|
|
703
|
+
try {
|
|
704
|
+
const projectRoot = process.cwd();
|
|
705
|
+
|
|
706
|
+
// Always create kit wrappers
|
|
707
|
+
const kitCmd = `@echo off\nnode "%~dp0node_modules\\add-skill-kit\\bin\\kit.js" %*`;
|
|
708
|
+
const kitSh = `#!/bin/sh\nnode "$(dirname "$0")/node_modules/add-skill-kit/bin/kit.js" "$@"`;
|
|
709
|
+
fs.writeFileSync(path.join(projectRoot, "kit.cmd"), kitCmd);
|
|
710
|
+
fs.writeFileSync(path.join(projectRoot, "kit"), kitSh, { mode: 0o755 });
|
|
711
|
+
|
|
712
|
+
if (installAutoLearn) {
|
|
713
|
+
// Create agent wrappers only if AutoLearn enabled
|
|
714
|
+
const agentCmd = `@echo off\nnode "%~dp0node_modules\\add-skill-kit\\lib\\agent-cli\\bin\\agent.js" %*`;
|
|
715
|
+
const agentSh = `#!/bin/sh\nnode "$(dirname "$0")/node_modules/add-skill-kit/lib/agent-cli/bin/agent.js" "$@"`;
|
|
716
|
+
fs.writeFileSync(path.join(projectRoot, "agent.cmd"), agentCmd);
|
|
717
|
+
fs.writeFileSync(path.join(projectRoot, "agent"), agentSh, { mode: 0o755 });
|
|
718
|
+
|
|
719
|
+
step(c.green("✓ Created wrapper scripts: agent, kit"));
|
|
720
|
+
step(c.dim("Run directly: ./agent or ./kit (Unix) | agent or kit (Windows)"));
|
|
721
|
+
} else {
|
|
722
|
+
step(c.green("✓ Created wrapper script: kit"));
|
|
723
|
+
step(c.dim("Run directly: ./kit (Unix) | kit (Windows)"));
|
|
724
|
+
}
|
|
725
|
+
} catch (wrapperErr) {
|
|
726
|
+
step(c.dim("Run: npx kit"));
|
|
727
|
+
}
|
|
674
728
|
} catch (e) {
|
|
675
729
|
cliSpinner.stop(c.yellow("Local CLI installation skipped"));
|
|
676
730
|
step(c.dim(`Run manually: npm i -D ${cliPackage}`));
|
|
677
731
|
}
|
|
678
732
|
}
|
|
679
733
|
|
|
734
|
+
// Run npm install to ensure all skill dependencies are available
|
|
735
|
+
stepLine();
|
|
736
|
+
const depsSpinner = spinner();
|
|
737
|
+
depsSpinner.start("Installing skill dependencies (csv-parse, etc.)");
|
|
738
|
+
try {
|
|
739
|
+
await execAsync("npm install", { timeout: 120000 });
|
|
740
|
+
depsSpinner.stop("Skill dependencies installed");
|
|
741
|
+
} catch (e) {
|
|
742
|
+
depsSpinner.stop(c.yellow("Dependencies installation skipped"));
|
|
743
|
+
step(c.dim("Run manually: npm install"));
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Install Python dependencies if requirements.txt exists
|
|
747
|
+
const pythonReqPath = path.join(WORKSPACE, "..", "skills", "requirements.txt");
|
|
748
|
+
if (fs.existsSync(pythonReqPath)) {
|
|
749
|
+
stepLine();
|
|
750
|
+
const pySpinner = spinner();
|
|
751
|
+
pySpinner.start("Installing Python dependencies (PyYAML, etc.)");
|
|
752
|
+
try {
|
|
753
|
+
await execAsync(`pip install -r "${pythonReqPath}"`, { timeout: 120000 });
|
|
754
|
+
pySpinner.stop("Python dependencies installed");
|
|
755
|
+
} catch (e) {
|
|
756
|
+
pySpinner.stop(c.yellow("Python dependencies skipped"));
|
|
757
|
+
step(c.dim(`Run manually: pip install -r .agent/skills/requirements.txt`));
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
680
761
|
stepLine();
|
|
681
762
|
console.log(` ${c.cyan("Done!")}`);
|
|
682
763
|
console.log();
|
package/bin/lib/ui.js
CHANGED
|
@@ -177,7 +177,7 @@ export function brandedIntro(version, status = "") {
|
|
|
177
177
|
// Last line: gradient ASCII + dim version (aligned at bottom)
|
|
178
178
|
const lastLine = bannerLines[bannerLines.length - 1];
|
|
179
179
|
console.log(pikaGradient(lastLine) + ` ${c.dim(`v${version}`)}`);
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
|
|
182
182
|
if (status) {
|
|
183
183
|
console.log(`${c.dim(status)}`);
|
|
@@ -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
|
+
});
|