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.
- package/README.md +179 -119
- package/bin/lib/commands/help.js +0 -4
- package/bin/lib/commands/install.js +129 -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 +12 -6
- package/lib/agent-cli/lib/auto_preview.py +0 -148
- package/lib/agent-cli/lib/checklist.py +0 -222
- package/lib/agent-cli/lib/session_manager.py +0 -120
- package/lib/agent-cli/lib/verify_all.py +0 -327
- /package/bin/{cli.js → kit.js} +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SignalRepository - Data Access Layer
|
|
3
|
+
*
|
|
4
|
+
* Manages persistence of evolution signals.
|
|
5
|
+
* Provides CRUD operations with storage abstraction.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EvolutionSignal } from '../../core/evolution/evolution-signal.js';
|
|
9
|
+
|
|
10
|
+
export class SignalRepository {
|
|
11
|
+
constructor(storage) {
|
|
12
|
+
this.storage = storage;
|
|
13
|
+
this.storageKey = 'evolution-signals';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Load all signals from storage
|
|
18
|
+
* @returns {Promise<Array<EvolutionSignal>>}
|
|
19
|
+
*/
|
|
20
|
+
async findAll() {
|
|
21
|
+
try {
|
|
22
|
+
const data = await this.storage.read(this.storageKey);
|
|
23
|
+
|
|
24
|
+
// Restore EvolutionSignal instances with methods
|
|
25
|
+
return (data?.signals || []).map(s => {
|
|
26
|
+
const signal = new EvolutionSignal(s.lessonId, s.reason, s.confidence, s.metadata);
|
|
27
|
+
signal.id = s.id;
|
|
28
|
+
signal.status = s.status;
|
|
29
|
+
signal.createdAt = s.createdAt;
|
|
30
|
+
signal.resolvedAt = s.resolvedAt;
|
|
31
|
+
return signal;
|
|
32
|
+
});
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error('Failed to load signals:', error.message);
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Find pending signals
|
|
41
|
+
* @returns {Promise<Array<EvolutionSignal>>}
|
|
42
|
+
*/
|
|
43
|
+
async findPending() {
|
|
44
|
+
const all = await this.findAll();
|
|
45
|
+
return all.filter(s => s.isPending());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Find signals by lesson ID
|
|
50
|
+
* @param {string} lessonId
|
|
51
|
+
* @returns {Promise<Array<EvolutionSignal>>}
|
|
52
|
+
*/
|
|
53
|
+
async findByLesson(lessonId) {
|
|
54
|
+
const all = await this.findAll();
|
|
55
|
+
return all.filter(s => s.lessonId === lessonId);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Find signal by ID
|
|
60
|
+
* @param {string} signalId
|
|
61
|
+
* @returns {Promise<EvolutionSignal|null>}
|
|
62
|
+
*/
|
|
63
|
+
async findById(signalId) {
|
|
64
|
+
const all = await this.findAll();
|
|
65
|
+
return all.find(s => s.id === signalId) || null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Save signal
|
|
70
|
+
* @param {EvolutionSignal} signal
|
|
71
|
+
* @returns {Promise<EvolutionSignal>}
|
|
72
|
+
*/
|
|
73
|
+
async save(signal) {
|
|
74
|
+
const all = await this.findAll();
|
|
75
|
+
const updated = [...all.filter(s => s.id !== signal.id), signal];
|
|
76
|
+
|
|
77
|
+
await this.storage.write(this.storageKey, {
|
|
78
|
+
signals: updated,
|
|
79
|
+
version: 1.0
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return signal;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Save all signals (batch operation)
|
|
87
|
+
* @param {Array<EvolutionSignal>} signals
|
|
88
|
+
*/
|
|
89
|
+
async saveAll(signals) {
|
|
90
|
+
await this.storage.write(this.storageKey, {
|
|
91
|
+
signals,
|
|
92
|
+
version: 1.0
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Delete signal
|
|
98
|
+
* @param {string} signalId
|
|
99
|
+
*/
|
|
100
|
+
async delete(signalId) {
|
|
101
|
+
const all = await this.findAll();
|
|
102
|
+
const updated = all.filter(s => s.id !== signalId);
|
|
103
|
+
await this.saveAll(updated);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Clean up old resolved signals (older than 30 days)
|
|
108
|
+
*/
|
|
109
|
+
async cleanup() {
|
|
110
|
+
const thirtyDaysAgo = Date.now() - (30 * 24 * 60 * 60 * 1000);
|
|
111
|
+
const all = await this.findAll();
|
|
112
|
+
|
|
113
|
+
const kept = all.filter(s =>
|
|
114
|
+
s.isPending() || s.resolvedAt > thirtyDaysAgo
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
await this.saveAll(kept);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JsonStorage - Storage Adapter
|
|
3
|
+
*
|
|
4
|
+
* JSON file storage implementation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
|
|
10
|
+
export class JsonStorage {
|
|
11
|
+
constructor(basePath) {
|
|
12
|
+
this.basePath = basePath;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Read JSON file
|
|
17
|
+
* @param {string} key - File identifier
|
|
18
|
+
* @returns {Promise<object>}
|
|
19
|
+
*/
|
|
20
|
+
async read(key) {
|
|
21
|
+
const filePath = path.join(this.basePath, `${key}.json`);
|
|
22
|
+
|
|
23
|
+
if (!fs.existsSync(filePath)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
28
|
+
return JSON.parse(content);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Write JSON file
|
|
33
|
+
* @param {string} key - File identifier
|
|
34
|
+
* @param {object} data - Data to write
|
|
35
|
+
*/
|
|
36
|
+
async write(key, data) {
|
|
37
|
+
fs.mkdirSync(this.basePath, { recursive: true });
|
|
38
|
+
|
|
39
|
+
const filePath = path.join(this.basePath, `${key}.json`);
|
|
40
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Delete JSON file
|
|
45
|
+
* @param {string} key - File identifier
|
|
46
|
+
*/
|
|
47
|
+
async delete(key) {
|
|
48
|
+
const filePath = path.join(this.basePath, `${key}.json`);
|
|
49
|
+
|
|
50
|
+
if (fs.existsSync(filePath)) {
|
|
51
|
+
fs.unlinkSync(filePath);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Check if file exists
|
|
57
|
+
* @param {string} key - File identifier
|
|
58
|
+
* @returns {Promise<boolean>}
|
|
59
|
+
*/
|
|
60
|
+
async exists(key) {
|
|
61
|
+
const filePath = path.join(this.basePath, `${key}.json`);
|
|
62
|
+
return fs.existsSync(filePath);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* YamlStorage - Storage Adapter for YAML Files
|
|
3
|
+
*
|
|
4
|
+
* Handles YAML file read/write operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import yaml from 'js-yaml';
|
|
10
|
+
|
|
11
|
+
export class YamlStorage {
|
|
12
|
+
constructor(basePath) {
|
|
13
|
+
this.basePath = basePath;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Read YAML file
|
|
18
|
+
* @param {string} key - File identifier
|
|
19
|
+
* @returns {Promise<object>}
|
|
20
|
+
*/
|
|
21
|
+
async read(key) {
|
|
22
|
+
const filePath = path.join(this.basePath, `${key}.yaml`);
|
|
23
|
+
|
|
24
|
+
if (!fs.existsSync(filePath)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
29
|
+
return yaml.load(content);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Write YAML file
|
|
34
|
+
* @param {string} key - File identifier
|
|
35
|
+
* @param {object} data - Data to write
|
|
36
|
+
*/
|
|
37
|
+
async write(key, data) {
|
|
38
|
+
fs.mkdirSync(this.basePath, { recursive: true });
|
|
39
|
+
|
|
40
|
+
const filePath = path.join(this.basePath, `${key}.yaml`);
|
|
41
|
+
const yamlStr = yaml.dump(data, { lineWidth: -1, quotingType: '"' });
|
|
42
|
+
fs.writeFileSync(filePath, yamlStr, 'utf8');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Delete YAML file
|
|
47
|
+
* @param {string} key - File identifier
|
|
48
|
+
*/
|
|
49
|
+
async delete(key) {
|
|
50
|
+
const filePath = path.join(this.basePath, `${key}.yaml`);
|
|
51
|
+
|
|
52
|
+
if (fs.existsSync(filePath)) {
|
|
53
|
+
fs.unlinkSync(filePath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if file exists
|
|
59
|
+
* @param {string} key - File identifier
|
|
60
|
+
* @returns {Promise<boolean>}
|
|
61
|
+
*/
|
|
62
|
+
async exists(key) {
|
|
63
|
+
const filePath = path.join(this.basePath, `${key}.yaml`);
|
|
64
|
+
return fs.existsSync(filePath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure Layer
|
|
3
|
+
*
|
|
4
|
+
* Cross-cutting concerns: config, logging, dependency injection.
|
|
5
|
+
*
|
|
6
|
+
* Exported:
|
|
7
|
+
* - config: Application configuration
|
|
8
|
+
* - di: Dependency injection container
|
|
9
|
+
* - logging: Logger utilities
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export * from './config/index.js';
|
|
13
|
+
export * from './di/index.js';
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillFormatter - Presentation Layer
|
|
3
|
+
*
|
|
4
|
+
* Formats cognitive lessons into Gemini-compatible skill markdown.
|
|
5
|
+
* Pure formatter - no business logic, no side effects.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class SkillFormatter {
|
|
9
|
+
/**
|
|
10
|
+
* Format a cognitive lesson as a Gemini skill
|
|
11
|
+
* @param {object} lesson - Cognitive lesson from LessonSynthesizer
|
|
12
|
+
* @returns {{name: string, filename: string, content: string}}
|
|
13
|
+
*/
|
|
14
|
+
format(lesson) {
|
|
15
|
+
const name = this.sanitizeName(lesson.tag);
|
|
16
|
+
const frontmatter = this.buildFrontmatter(lesson);
|
|
17
|
+
const content = this.buildContent(lesson);
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
name,
|
|
21
|
+
filename: `learned-${name}.md`,
|
|
22
|
+
content: `---\n${frontmatter}\n---\n\n${content}`
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sanitize tag as filename-safe name
|
|
28
|
+
*/
|
|
29
|
+
sanitizeName(tag) {
|
|
30
|
+
return tag.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Build YAML frontmatter
|
|
35
|
+
*/
|
|
36
|
+
buildFrontmatter(lesson) {
|
|
37
|
+
const totalHits = this.calculateTotalHits(lesson);
|
|
38
|
+
const evidenceCount = lesson.mistakes.length + lesson.improvements.length;
|
|
39
|
+
|
|
40
|
+
return [
|
|
41
|
+
`name: ${lesson.tag}`,
|
|
42
|
+
`description: ${lesson.intent.goal}`,
|
|
43
|
+
`maturity: ${lesson.maturity.state}`,
|
|
44
|
+
`confidence: ${lesson.maturity.confidence}`,
|
|
45
|
+
`evidence: ${evidenceCount} patterns, ${totalHits} detections`,
|
|
46
|
+
`trigger: always_on`
|
|
47
|
+
].join('\n');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Calculate total hits across mistakes and improvements
|
|
52
|
+
*/
|
|
53
|
+
calculateTotalHits(lesson) {
|
|
54
|
+
const mistakeHits = lesson.mistakes.reduce((sum, m) => sum + (m.hitCount || 0), 0);
|
|
55
|
+
const improvementHits = lesson.improvements.reduce((sum, i) => sum + (i.appliedCount || 0), 0);
|
|
56
|
+
return mistakeHits + improvementHits;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Build main content
|
|
61
|
+
*/
|
|
62
|
+
buildContent(lesson) {
|
|
63
|
+
let md = `# ${lesson.title}\n\n`;
|
|
64
|
+
|
|
65
|
+
// Header section
|
|
66
|
+
md += this.buildHeader(lesson);
|
|
67
|
+
|
|
68
|
+
// Anti-patterns section
|
|
69
|
+
if (lesson.mistakes.length > 0) {
|
|
70
|
+
md += this.buildAntiPatterns(lesson.mistakes);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Best practices section
|
|
74
|
+
if (lesson.improvements.length > 0) {
|
|
75
|
+
md += this.buildBestPractices(lesson.improvements);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Evolution status
|
|
79
|
+
md += this.buildEvolutionStatus(lesson);
|
|
80
|
+
|
|
81
|
+
// When to apply
|
|
82
|
+
md += this.buildApplicationGuide(lesson);
|
|
83
|
+
|
|
84
|
+
// Confidence metrics
|
|
85
|
+
md += this.buildMetrics(lesson);
|
|
86
|
+
|
|
87
|
+
// Footer
|
|
88
|
+
md += this.buildFooter();
|
|
89
|
+
|
|
90
|
+
return md;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build header section
|
|
95
|
+
*/
|
|
96
|
+
buildHeader(lesson) {
|
|
97
|
+
return [
|
|
98
|
+
`> **Intent:** ${lesson.intent.goal}`,
|
|
99
|
+
`> **Maturity:** ${lesson.maturity.state} (${(lesson.maturity.confidence * 100).toFixed(0)}% confidence)`,
|
|
100
|
+
`> **Coverage:** ${lesson.maturity.coverage}`,
|
|
101
|
+
'',
|
|
102
|
+
`**Recommendation:** ${lesson.maturity.recommendation}`,
|
|
103
|
+
'',
|
|
104
|
+
''
|
|
105
|
+
].join('\n');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Build anti-patterns section
|
|
110
|
+
*/
|
|
111
|
+
buildAntiPatterns(mistakes) {
|
|
112
|
+
let md = `## 🚫 Anti-Patterns (Avoid These)\n\n`;
|
|
113
|
+
|
|
114
|
+
mistakes.forEach(mistake => {
|
|
115
|
+
md += `### ❌ ${mistake.id}: ${mistake.message}\n\n`;
|
|
116
|
+
md += `**Pattern:** \`${mistake.pattern}\` \n`;
|
|
117
|
+
md += `**Severity:** ${mistake.severity} \n`;
|
|
118
|
+
md += `**Hit Count:** ${mistake.hitCount || 0} detections \n`;
|
|
119
|
+
|
|
120
|
+
if (mistake.lastHit) {
|
|
121
|
+
const lastHit = new Date(mistake.lastHit).toLocaleDateString();
|
|
122
|
+
md += `**Last Seen:** ${lastHit} \n`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
md += '\n';
|
|
126
|
+
|
|
127
|
+
// Add tags if available
|
|
128
|
+
if (mistake.tags && mistake.tags.length > 0) {
|
|
129
|
+
md += `**Tags:** ${mistake.tags.join(', ')} \n\n`;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return md;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Build best practices section
|
|
138
|
+
*/
|
|
139
|
+
buildBestPractices(improvements) {
|
|
140
|
+
let md = `## ✅ Best Practices (Do This Instead)\n\n`;
|
|
141
|
+
|
|
142
|
+
improvements.forEach(improvement => {
|
|
143
|
+
md += `### ✅ ${improvement.id}: ${improvement.message}\n\n`;
|
|
144
|
+
md += `**Pattern:** \`${improvement.pattern}\` \n`;
|
|
145
|
+
md += `**Applied Count:** ${improvement.appliedCount || 0} times \n`;
|
|
146
|
+
|
|
147
|
+
if (improvement.lastApplied) {
|
|
148
|
+
const lastApplied = new Date(improvement.lastApplied).toLocaleDateString();
|
|
149
|
+
md += `**Last Applied:** ${lastApplied} \n`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
md += '\n';
|
|
153
|
+
|
|
154
|
+
// Add tags if available
|
|
155
|
+
if (improvement.tags && improvement.tags.length > 0) {
|
|
156
|
+
md += `**Tags:** ${improvement.tags.join(', ')} \n\n`;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return md;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Build evolution status section
|
|
165
|
+
*/
|
|
166
|
+
buildEvolutionStatus(lesson) {
|
|
167
|
+
let md = `## 📊 Evolution Status\n\n`;
|
|
168
|
+
|
|
169
|
+
if (lesson.evolution.signals && lesson.evolution.signals.length > 0) {
|
|
170
|
+
md += `**Active Signals:**\n`;
|
|
171
|
+
lesson.evolution.signals.forEach(signal => {
|
|
172
|
+
md += `- **${signal.type}** (${signal.priority}): ${signal.reason}\n`;
|
|
173
|
+
});
|
|
174
|
+
md += '\n';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (lesson.evolution.missingAreas && lesson.evolution.missingAreas.length > 0) {
|
|
178
|
+
md += `**Missing Areas:**\n`;
|
|
179
|
+
lesson.evolution.missingAreas.forEach(area => {
|
|
180
|
+
md += `- ${area.area}: ${area.reason}\n`;
|
|
181
|
+
});
|
|
182
|
+
md += '\n';
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
md += `**Next Action:** ${lesson.evolution.nextAction}\n\n`;
|
|
186
|
+
|
|
187
|
+
return md;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Build application guide
|
|
192
|
+
*/
|
|
193
|
+
buildApplicationGuide(lesson) {
|
|
194
|
+
let md = `## 🎯 When to Apply\n\n`;
|
|
195
|
+
md += `This skill applies when:\n`;
|
|
196
|
+
md += `- Working with **${lesson.intent.category}**\n`;
|
|
197
|
+
md += `- Goal: ${lesson.intent.goal}\n`;
|
|
198
|
+
md += `- Confidence level: ${lesson.intent.strength >= 0.7 ? 'High' : 'Moderate'}\n\n`;
|
|
199
|
+
|
|
200
|
+
return md;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Build confidence metrics
|
|
205
|
+
*/
|
|
206
|
+
buildMetrics(lesson) {
|
|
207
|
+
let md = `## 📈 Confidence Metrics\n\n`;
|
|
208
|
+
|
|
209
|
+
const indicators = lesson.maturity.indicators;
|
|
210
|
+
md += `- **Balance Score:** ${(indicators.balance * 100).toFixed(0)}% (improvement/total ratio)\n`;
|
|
211
|
+
md += `- **Evidence Score:** ${(indicators.evidence * 100).toFixed(0)}% (validation from hits)\n`;
|
|
212
|
+
md += `- **Recency Score:** ${(indicators.recency * 100).toFixed(0)}% (freshness)\n`;
|
|
213
|
+
md += `- **Overall Confidence:** ${(lesson.maturity.confidence * 100).toFixed(0)}%\n\n`;
|
|
214
|
+
|
|
215
|
+
return md;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Build footer
|
|
220
|
+
*/
|
|
221
|
+
buildFooter() {
|
|
222
|
+
const timestamp = new Date().toISOString();
|
|
223
|
+
|
|
224
|
+
return [
|
|
225
|
+
'---',
|
|
226
|
+
'',
|
|
227
|
+
'*Auto-generated from Agent Skill Kit* ',
|
|
228
|
+
`*Source: \`.agent/knowledge/mistakes.yaml\` + \`improvements.yaml\`* `,
|
|
229
|
+
`*Generated: ${timestamp}*`
|
|
230
|
+
].join('\n');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ExportService - Application Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates skill export workflow:
|
|
5
|
+
* - Load cognitive lessons
|
|
6
|
+
* - Filter by maturity
|
|
7
|
+
* - Format as Gemini skills
|
|
8
|
+
* - Write to .agent/skills/
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
|
|
14
|
+
export class ExportService {
|
|
15
|
+
constructor(learningService, skillFormatter) {
|
|
16
|
+
this.learningService = learningService;
|
|
17
|
+
this.skillFormatter = skillFormatter;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Export all mature skills to Gemini format
|
|
22
|
+
* @param {string} outputDir - Output directory (default: .agent/skills)
|
|
23
|
+
* @param {number} minConfidence - Minimum confidence threshold (default: 0.8)
|
|
24
|
+
* @returns {Promise<Array>} Exported skills
|
|
25
|
+
*/
|
|
26
|
+
async exportMatureSkills(outputDir = '.agent/skills', minConfidence = 0.8) {
|
|
27
|
+
// 1. Get all cognitive lessons
|
|
28
|
+
const lessons = await this.learningService.getCognitiveLessons();
|
|
29
|
+
|
|
30
|
+
// 2. Filter mature lessons
|
|
31
|
+
const mature = this.filterMature(lessons, minConfidence);
|
|
32
|
+
|
|
33
|
+
// 3. Format as skills
|
|
34
|
+
const skills = mature.map(lesson =>
|
|
35
|
+
this.skillFormatter.format(lesson)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// 4. Write to output directory
|
|
39
|
+
const exported = await this.writeSkills(skills, outputDir);
|
|
40
|
+
|
|
41
|
+
return exported;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Export a specific lesson by ID
|
|
46
|
+
* @param {string} lessonId - Lesson ID (e.g., LESSON-008)
|
|
47
|
+
* @param {string} outputDir - Output directory
|
|
48
|
+
* @returns {Promise<object>} Exported skill
|
|
49
|
+
*/
|
|
50
|
+
async exportLesson(lessonId, outputDir = '.agent/skills') {
|
|
51
|
+
// 1. Get all lessons
|
|
52
|
+
const lessons = await this.learningService.getCognitiveLessons();
|
|
53
|
+
|
|
54
|
+
// 2. Find specific lesson
|
|
55
|
+
const lesson = lessons.find(l => l.id === lessonId);
|
|
56
|
+
|
|
57
|
+
if (!lesson) {
|
|
58
|
+
throw new Error(`Lesson not found: ${lessonId}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Format as skill
|
|
62
|
+
const skill = this.skillFormatter.format(lesson);
|
|
63
|
+
|
|
64
|
+
// 4. Write to output
|
|
65
|
+
await this.writeSkill(skill, outputDir);
|
|
66
|
+
|
|
67
|
+
return skill;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Preview mature skills without writing
|
|
72
|
+
* @param {number} minConfidence - Minimum confidence threshold
|
|
73
|
+
* @returns {Promise<Array>} Skills preview
|
|
74
|
+
*/
|
|
75
|
+
async previewMatureSkills(minConfidence = 0.8) {
|
|
76
|
+
const lessons = await this.learningService.getCognitiveLessons();
|
|
77
|
+
const mature = this.filterMature(lessons, minConfidence);
|
|
78
|
+
|
|
79
|
+
return mature.map(lesson => ({
|
|
80
|
+
id: lesson.id,
|
|
81
|
+
title: lesson.title,
|
|
82
|
+
state: lesson.maturity.state,
|
|
83
|
+
confidence: lesson.maturity.confidence,
|
|
84
|
+
coverage: lesson.maturity.coverage,
|
|
85
|
+
filename: `learned-${this.skillFormatter.sanitizeName(lesson.tag)}.md`
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Filter mature lessons
|
|
91
|
+
* @param {Array} lessons - All cognitive lessons
|
|
92
|
+
* @param {number} minConfidence - Minimum confidence
|
|
93
|
+
* @returns {Array} Filtered lessons
|
|
94
|
+
*/
|
|
95
|
+
filterMature(lessons, minConfidence) {
|
|
96
|
+
return lessons.filter(lesson => {
|
|
97
|
+
const isMature = lesson.maturity.state === 'MATURE' || lesson.maturity.state === 'IDEAL';
|
|
98
|
+
const isConfident = lesson.maturity.confidence >= minConfidence;
|
|
99
|
+
return isMature && isConfident;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Write multiple skills to directory
|
|
105
|
+
* @param {Array} skills - Formatted skills
|
|
106
|
+
* @param {string} outputDir - Output directory
|
|
107
|
+
* @returns {Promise<Array>} Written skills with paths
|
|
108
|
+
*/
|
|
109
|
+
async writeSkills(skills, outputDir) {
|
|
110
|
+
// Ensure directory exists
|
|
111
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
112
|
+
|
|
113
|
+
const written = [];
|
|
114
|
+
|
|
115
|
+
for (const skill of skills) {
|
|
116
|
+
const filePath = path.join(outputDir, skill.filename);
|
|
117
|
+
fs.writeFileSync(filePath, skill.content, 'utf8');
|
|
118
|
+
|
|
119
|
+
written.push({
|
|
120
|
+
...skill,
|
|
121
|
+
path: filePath
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return written;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Write a single skill to directory
|
|
130
|
+
* @param {object} skill - Formatted skill
|
|
131
|
+
* @param {string} outputDir - Output directory
|
|
132
|
+
*/
|
|
133
|
+
async writeSkill(skill, outputDir) {
|
|
134
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
135
|
+
|
|
136
|
+
const filePath = path.join(outputDir, skill.filename);
|
|
137
|
+
fs.writeFileSync(filePath, skill.content, 'utf8');
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
...skill,
|
|
141
|
+
path: filePath
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get export statistics
|
|
147
|
+
* @returns {Promise<object>} Export stats
|
|
148
|
+
*/
|
|
149
|
+
async getExportStats() {
|
|
150
|
+
const lessons = await this.learningService.getCognitiveLessons();
|
|
151
|
+
const mature = this.filterMature(lessons, 0.8);
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
total: lessons.length,
|
|
155
|
+
mature: mature.length,
|
|
156
|
+
learning: lessons.filter(l => l.maturity.state === 'LEARNING').length,
|
|
157
|
+
raw: lessons.filter(l => l.maturity.state === 'RAW').length,
|
|
158
|
+
ideal: lessons.filter(l => l.maturity.state === 'IDEAL').length,
|
|
159
|
+
exportable: mature.length
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Services Layer
|
|
3
|
+
*
|
|
4
|
+
* Application services that orchestrate core domain logic.
|
|
5
|
+
* Services have NO business logic, only coordination.
|
|
6
|
+
*
|
|
7
|
+
* Exported:
|
|
8
|
+
* - LearningService
|
|
9
|
+
* - ExportService
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { LearningService } from './learning-service.js';
|
|
13
|
+
export { ExportService } from './export-service.js';
|