add-skill-kit 3.2.4 → 3.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +179 -119
  2. package/bin/lib/commands/help.js +0 -4
  3. package/bin/lib/commands/install.js +129 -9
  4. package/bin/lib/ui.js +1 -1
  5. package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
  6. package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
  7. package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
  8. package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
  9. package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
  10. package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
  11. package/lib/agent-cli/bin/{ag-smart.js → agent.js} +48 -15
  12. package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
  13. package/lib/agent-cli/dashboard/index.html +538 -0
  14. package/lib/agent-cli/lib/audit.js +2 -2
  15. package/lib/agent-cli/lib/auto-learn.js +8 -8
  16. package/lib/agent-cli/lib/eslint-fix.js +1 -1
  17. package/lib/agent-cli/lib/fix.js +5 -5
  18. package/lib/agent-cli/lib/hooks/install-hooks.js +4 -4
  19. package/lib/agent-cli/lib/hooks/lint-learn.js +4 -4
  20. package/lib/agent-cli/lib/learn.js +10 -10
  21. package/lib/agent-cli/lib/recall.js +1 -1
  22. package/lib/agent-cli/lib/settings.js +24 -0
  23. package/lib/agent-cli/lib/skill-learn.js +2 -2
  24. package/lib/agent-cli/lib/stats.js +3 -3
  25. package/lib/agent-cli/lib/ui/dashboard-ui.js +103 -4
  26. package/lib/agent-cli/lib/ui/index.js +36 -6
  27. package/lib/agent-cli/lib/watcher.js +2 -2
  28. package/lib/agent-cli/package.json +4 -4
  29. package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
  30. package/lib/agent-cli/scripts/dashboard_server.js +224 -0
  31. package/lib/agent-cli/scripts/error_sensor.js +565 -0
  32. package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
  33. package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
  34. package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
  35. package/lib/agent-cli/scripts/rule_sharing.js +374 -0
  36. package/lib/agent-cli/scripts/skill_injector.js +387 -0
  37. package/lib/agent-cli/scripts/success_sensor.js +500 -0
  38. package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
  39. package/lib/agent-cli/services/auto-learn-service.js +247 -0
  40. package/lib/agent-cli/src/MIGRATION.md +418 -0
  41. package/lib/agent-cli/src/README.md +367 -0
  42. package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
  43. package/lib/agent-cli/src/core/evolution/index.js +17 -0
  44. package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
  45. package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
  46. package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
  47. package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
  48. package/lib/agent-cli/src/core/index.js +15 -0
  49. package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
  50. package/lib/agent-cli/src/core/learning/index.js +12 -0
  51. package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
  52. package/lib/agent-cli/src/core/scanning/index.js +14 -0
  53. package/lib/agent-cli/src/data/index.js +13 -0
  54. package/lib/agent-cli/src/data/repositories/index.js +8 -0
  55. package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
  56. package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
  57. package/lib/agent-cli/src/data/storage/index.js +8 -0
  58. package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
  59. package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
  60. package/lib/agent-cli/src/infrastructure/index.js +13 -0
  61. package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
  62. package/lib/agent-cli/src/services/export-service.js +162 -0
  63. package/lib/agent-cli/src/services/index.js +13 -0
  64. package/lib/agent-cli/src/services/learning-service.js +99 -0
  65. package/lib/agent-cli/types/index.d.ts +343 -0
  66. package/lib/agent-cli/utils/benchmark.js +269 -0
  67. package/lib/agent-cli/utils/logger.js +303 -0
  68. package/lib/agent-cli/utils/ml_patterns.js +300 -0
  69. package/lib/agent-cli/utils/recovery.js +312 -0
  70. package/lib/agent-cli/utils/telemetry.js +290 -0
  71. package/lib/agentskillskit-cli/ag-smart.js +15 -15
  72. package/lib/agentskillskit-cli/package.json +3 -3
  73. package/package.json +12 -6
  74. package/lib/agent-cli/lib/auto_preview.py +0 -148
  75. package/lib/agent-cli/lib/checklist.py +0 -222
  76. package/lib/agent-cli/lib/session_manager.py +0 -120
  77. package/lib/agent-cli/lib/verify_all.py +0 -327
  78. /package/bin/{cli.js → kit.js} +0 -0
@@ -0,0 +1,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,8 @@
1
+ /**
2
+ * Storage Adapters - Data Layer
3
+ *
4
+ * Storage implementations for different file formats.
5
+ */
6
+
7
+ export { JsonStorage } from './json-storage.js';
8
+ export { YamlStorage } from './yaml-storage.js';
@@ -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';