pikakit 1.0.7 → 1.0.9
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 +10 -6
- package/bin/kit.mjs +6 -2
- package/bin/lib/commands/help.js +13 -8
- package/bin/lib/config.js +4 -2
- package/lib/agent-cli/lib/ab-testing.js +508 -0
- package/lib/agent-cli/lib/causality-engine.js +623 -0
- package/lib/agent-cli/lib/dashboard-data.js +365 -0
- package/lib/agent-cli/lib/fix.js +1 -1
- package/lib/agent-cli/lib/metrics-collector.js +523 -0
- package/lib/agent-cli/lib/metrics-schema.js +410 -0
- package/lib/agent-cli/lib/precision-skill-generator.js +584 -0
- package/lib/agent-cli/lib/recall.js +1 -1
- package/lib/agent-cli/lib/reinforcement.js +610 -0
- package/lib/agent-cli/lib/ui/index.js +37 -14
- package/package.json +4 -2
- package/lib/agent-cli/lib/auto-learn.js +0 -319
- package/lib/agent-cli/scripts/adaptive_engine.js +0 -381
- package/lib/agent-cli/scripts/error_sensor.js +0 -565
- package/lib/agent-cli/scripts/learn_from_failure.js +0 -225
- package/lib/agent-cli/scripts/pattern_analyzer.js +0 -781
- package/lib/agent-cli/scripts/skill_injector.js +0 -387
- package/lib/agent-cli/scripts/success_sensor.js +0 -500
- package/lib/agent-cli/scripts/user_correction_sensor.js +0 -426
- package/lib/agent-cli/services/auto-learn-service.js +0 -247
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AutoLearn v6.0 - Precision Skill Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates accurate, context-aware skills from patterns.
|
|
5
|
+
* Uses context fingerprinting and evidence validation.
|
|
6
|
+
*
|
|
7
|
+
* Key concepts:
|
|
8
|
+
* - Context Fingerprint: WHO + WHAT + WHERE + WHEN to apply
|
|
9
|
+
* - Evidence-Based: Requires minimum evidence before skill creation
|
|
10
|
+
* - Auto-Versioning: Skills have semantic versions
|
|
11
|
+
* - Lifecycle: Create → Evaluate → Prune
|
|
12
|
+
*
|
|
13
|
+
* @version 6.0.0
|
|
14
|
+
* @author PikaKit
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import path from 'path';
|
|
19
|
+
import { recordSkillEvent } from './metrics-collector.js';
|
|
20
|
+
import { shouldPromote, getReinforcementState } from './reinforcement.js';
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// CONFIGURATION
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
const SKILLS_DIR = path.join(process.cwd(), '.agent', 'skills', 'auto-generated');
|
|
27
|
+
const SKILL_REGISTRY = path.join(SKILLS_DIR, 'registry.json');
|
|
28
|
+
|
|
29
|
+
// Minimum requirements for skill generation
|
|
30
|
+
const SKILL_REQUIREMENTS = {
|
|
31
|
+
MIN_CONFIDENCE: 0.80,
|
|
32
|
+
MIN_EVIDENCE: 5,
|
|
33
|
+
MIN_POSITIVE_REINFORCEMENTS: 3
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// CONTEXT FINGERPRINTING
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a context fingerprint for a pattern
|
|
42
|
+
* @param {Object} pattern - Pattern with context
|
|
43
|
+
* @returns {Object} - Context fingerprint
|
|
44
|
+
*/
|
|
45
|
+
export function createContextFingerprint(pattern) {
|
|
46
|
+
const context = pattern.context || {};
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
// WHO: What type of files/code
|
|
50
|
+
filePatterns: context.fileTypes || extractFilePatterns(pattern),
|
|
51
|
+
language: context.language || detectLanguage(pattern),
|
|
52
|
+
|
|
53
|
+
// WHAT: What is being detected
|
|
54
|
+
patternType: pattern.patternType || 'general',
|
|
55
|
+
category: pattern.category || categorizePattern(pattern),
|
|
56
|
+
|
|
57
|
+
// WHERE: In what context
|
|
58
|
+
framework: context.framework || null,
|
|
59
|
+
library: context.library || null,
|
|
60
|
+
domain: context.domain || null,
|
|
61
|
+
|
|
62
|
+
// WHEN: Conditions for application
|
|
63
|
+
conditions: extractConditions(pattern),
|
|
64
|
+
excludePaths: pattern.excludePaths || [],
|
|
65
|
+
|
|
66
|
+
// HOW CONFIDENT
|
|
67
|
+
confidence: pattern.confidence,
|
|
68
|
+
evidenceCount: pattern.evidence?.length || pattern.hitCount || 0
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Extract file patterns from pattern regex
|
|
74
|
+
*/
|
|
75
|
+
function extractFilePatterns(pattern) {
|
|
76
|
+
const patterns = ['*.js', '*.ts', '*.tsx', '*.jsx'];
|
|
77
|
+
|
|
78
|
+
// Check for specific file type hints
|
|
79
|
+
if (pattern.pattern?.includes('tsx') || pattern.pattern?.includes('jsx')) {
|
|
80
|
+
return ['*.tsx', '*.jsx'];
|
|
81
|
+
}
|
|
82
|
+
if (pattern.pattern?.includes('import')) {
|
|
83
|
+
return patterns;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return patterns;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Detect language from pattern
|
|
91
|
+
*/
|
|
92
|
+
function detectLanguage(pattern) {
|
|
93
|
+
const p = pattern.pattern || '';
|
|
94
|
+
|
|
95
|
+
if (p.includes('interface') || p.includes(': ')) return 'typescript';
|
|
96
|
+
if (p.includes('useState') || p.includes('useEffect')) return 'react';
|
|
97
|
+
if (p.includes('def ') || p.includes('import ')) return 'python';
|
|
98
|
+
|
|
99
|
+
return 'javascript';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Categorize pattern based on content
|
|
104
|
+
*/
|
|
105
|
+
function categorizePattern(pattern) {
|
|
106
|
+
const p = (pattern.pattern || '').toLowerCase();
|
|
107
|
+
const m = (pattern.message || '').toLowerCase();
|
|
108
|
+
|
|
109
|
+
if (p.includes('security') || m.includes('security') || p.includes('eval')) {
|
|
110
|
+
return 'security';
|
|
111
|
+
}
|
|
112
|
+
if (p.includes('import') || p.includes('require')) {
|
|
113
|
+
return 'dependency';
|
|
114
|
+
}
|
|
115
|
+
if (p.includes('console') || p.includes('debugger')) {
|
|
116
|
+
return 'quality';
|
|
117
|
+
}
|
|
118
|
+
if (p.includes('async') || p.includes('await')) {
|
|
119
|
+
return 'async';
|
|
120
|
+
}
|
|
121
|
+
if (p.includes('type') || p.includes('interface')) {
|
|
122
|
+
return 'type_safety';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return 'general';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Extract conditions from pattern
|
|
130
|
+
*/
|
|
131
|
+
function extractConditions(pattern) {
|
|
132
|
+
const conditions = [];
|
|
133
|
+
|
|
134
|
+
if (pattern.cause) {
|
|
135
|
+
conditions.push({ type: 'cause', value: pattern.cause });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (pattern.context?.framework) {
|
|
139
|
+
conditions.push({ type: 'framework', value: pattern.context.framework });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return conditions;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ============================================================================
|
|
146
|
+
// SKILL GENERATION
|
|
147
|
+
// ============================================================================
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if pattern is ready to become a skill
|
|
151
|
+
* @param {Object} pattern - Pattern to check
|
|
152
|
+
* @returns {Object} - Readiness check result
|
|
153
|
+
*/
|
|
154
|
+
export function checkSkillReadiness(pattern) {
|
|
155
|
+
const checks = {
|
|
156
|
+
confidence: pattern.confidence >= SKILL_REQUIREMENTS.MIN_CONFIDENCE,
|
|
157
|
+
evidence: (pattern.evidence?.length || pattern.hitCount || 0) >= SKILL_REQUIREMENTS.MIN_EVIDENCE,
|
|
158
|
+
reinforcement: checkPositiveReinforcement(pattern)
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const ready = Object.values(checks).every(Boolean);
|
|
162
|
+
const missing = Object.entries(checks)
|
|
163
|
+
.filter(([, passed]) => !passed)
|
|
164
|
+
.map(([name]) => name);
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
ready,
|
|
168
|
+
checks,
|
|
169
|
+
missing,
|
|
170
|
+
requirements: SKILL_REQUIREMENTS
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if pattern has enough positive reinforcement
|
|
176
|
+
*/
|
|
177
|
+
function checkPositiveReinforcement(pattern) {
|
|
178
|
+
const history = pattern.reinforcementHistory || [];
|
|
179
|
+
const recentPositive = history.slice(-10).filter(r => r.delta > 0).length;
|
|
180
|
+
return recentPositive >= SKILL_REQUIREMENTS.MIN_POSITIVE_REINFORCEMENTS;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Generate a precision skill from a pattern
|
|
185
|
+
* @param {Object} pattern - Validated pattern
|
|
186
|
+
* @returns {Object} - Generated skill
|
|
187
|
+
*/
|
|
188
|
+
export function generateSkill(pattern) {
|
|
189
|
+
const readiness = checkSkillReadiness(pattern);
|
|
190
|
+
if (!readiness.ready) {
|
|
191
|
+
return { success: false, reason: 'Pattern not ready', missing: readiness.missing };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const fingerprint = createContextFingerprint(pattern);
|
|
195
|
+
const skillId = generateSkillId(pattern);
|
|
196
|
+
const version = '1.0.0';
|
|
197
|
+
|
|
198
|
+
const skill = {
|
|
199
|
+
// Metadata
|
|
200
|
+
id: skillId,
|
|
201
|
+
version,
|
|
202
|
+
createdAt: new Date().toISOString(),
|
|
203
|
+
updatedAt: new Date().toISOString(),
|
|
204
|
+
sourcePatternId: pattern.id,
|
|
205
|
+
|
|
206
|
+
// Human-readable
|
|
207
|
+
name: generateSkillName(pattern),
|
|
208
|
+
description: pattern.message || 'Auto-generated skill',
|
|
209
|
+
|
|
210
|
+
// Context Fingerprint (WHEN to apply)
|
|
211
|
+
appliesWhen: {
|
|
212
|
+
filePatterns: fingerprint.filePatterns,
|
|
213
|
+
language: fingerprint.language,
|
|
214
|
+
framework: fingerprint.framework,
|
|
215
|
+
conditions: fingerprint.conditions
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// Detection (WHAT to check)
|
|
219
|
+
detection: {
|
|
220
|
+
pattern: pattern.pattern,
|
|
221
|
+
patternType: fingerprint.patternType,
|
|
222
|
+
category: fingerprint.category
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// Root Cause (WHY it's important)
|
|
226
|
+
cause: {
|
|
227
|
+
root: pattern.cause || 'Learned from past experience',
|
|
228
|
+
effect: pattern.effect || 'May cause issues',
|
|
229
|
+
evidenceCount: fingerprint.evidenceCount
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// Fix (HOW to resolve)
|
|
233
|
+
fix: pattern.fix || pattern.autoFix || null,
|
|
234
|
+
|
|
235
|
+
// Metrics
|
|
236
|
+
metrics: {
|
|
237
|
+
confidence: pattern.confidence,
|
|
238
|
+
effectiveness: 0, // Tracked over time
|
|
239
|
+
timesApplied: 0,
|
|
240
|
+
timesHelped: 0,
|
|
241
|
+
lastReinforcement: pattern.lastReinforcement || null
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// Lifecycle
|
|
245
|
+
lifecycle: {
|
|
246
|
+
state: 'active',
|
|
247
|
+
stateChangedAt: new Date().toISOString(),
|
|
248
|
+
evaluationCount: 0
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// Generate SKILL.md content
|
|
253
|
+
const skillMd = generateSkillMarkdown(skill);
|
|
254
|
+
|
|
255
|
+
// Save skill
|
|
256
|
+
saveSkill(skill, skillMd);
|
|
257
|
+
|
|
258
|
+
// Record for metrics
|
|
259
|
+
recordSkillEvent({
|
|
260
|
+
type: 'created',
|
|
261
|
+
creationTime: 1 // Placeholder
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return { success: true, skill, skillMd };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Generate unique skill ID
|
|
269
|
+
*/
|
|
270
|
+
function generateSkillId(pattern) {
|
|
271
|
+
const category = categorizePattern(pattern);
|
|
272
|
+
const timestamp = Date.now().toString(36).toUpperCase();
|
|
273
|
+
return `SKILL-AUTO-${category.toUpperCase()}-${timestamp}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Generate human-readable skill name
|
|
278
|
+
*/
|
|
279
|
+
function generateSkillName(pattern) {
|
|
280
|
+
const category = categorizePattern(pattern);
|
|
281
|
+
const action = pattern.message?.split(' ').slice(0, 3).join(' ') || 'Auto-learned';
|
|
282
|
+
return `${category}: ${action}`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Generate SKILL.md content
|
|
287
|
+
*/
|
|
288
|
+
function generateSkillMarkdown(skill) {
|
|
289
|
+
return `---
|
|
290
|
+
name: ${skill.name}
|
|
291
|
+
id: ${skill.id}
|
|
292
|
+
version: ${skill.version}
|
|
293
|
+
description: ${skill.description}
|
|
294
|
+
triggers:
|
|
295
|
+
${skill.appliesWhen.filePatterns.map(p => ` - "${p}"`).join('\n')}
|
|
296
|
+
auto_generated: true
|
|
297
|
+
source_pattern: ${skill.sourcePatternId}
|
|
298
|
+
created: ${skill.createdAt}
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
# ${skill.name}
|
|
302
|
+
|
|
303
|
+
> Auto-generated skill from pattern learning
|
|
304
|
+
|
|
305
|
+
## When This Applies
|
|
306
|
+
|
|
307
|
+
| Condition | Value |
|
|
308
|
+
|-----------|-------|
|
|
309
|
+
| File Patterns | ${skill.appliesWhen.filePatterns.join(', ')} |
|
|
310
|
+
| Language | ${skill.appliesWhen.language || 'Any'} |
|
|
311
|
+
| Framework | ${skill.appliesWhen.framework || 'Any'} |
|
|
312
|
+
|
|
313
|
+
## What to Detect
|
|
314
|
+
|
|
315
|
+
\`\`\`regex
|
|
316
|
+
${skill.detection.pattern}
|
|
317
|
+
\`\`\`
|
|
318
|
+
|
|
319
|
+
**Category**: ${skill.detection.category}
|
|
320
|
+
|
|
321
|
+
## Why It Matters
|
|
322
|
+
|
|
323
|
+
**Root Cause**: ${skill.cause.root}
|
|
324
|
+
|
|
325
|
+
**Potential Effect**: ${skill.cause.effect}
|
|
326
|
+
|
|
327
|
+
**Evidence**: ${skill.cause.evidenceCount} occurrences recorded
|
|
328
|
+
|
|
329
|
+
## How to Fix
|
|
330
|
+
|
|
331
|
+
${skill.fix ? `
|
|
332
|
+
**Fix Type**: ${skill.fix.type || 'Manual'}
|
|
333
|
+
|
|
334
|
+
${skill.fix.details ? `\`\`\`
|
|
335
|
+
${JSON.stringify(skill.fix.details, null, 2)}
|
|
336
|
+
\`\`\`` : 'Follow the guidance above to resolve.'}
|
|
337
|
+
` : 'Manual review and correction required.'}
|
|
338
|
+
|
|
339
|
+
## Metrics
|
|
340
|
+
|
|
341
|
+
| Metric | Value |
|
|
342
|
+
|--------|-------|
|
|
343
|
+
| Confidence | ${(skill.metrics.confidence * 100).toFixed(1)}% |
|
|
344
|
+
| Times Applied | ${skill.metrics.timesApplied} |
|
|
345
|
+
| Effectiveness | ${(skill.metrics.effectiveness * 100).toFixed(1)}% |
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
*This skill was auto-generated by AutoLearn v6.0*
|
|
350
|
+
`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ============================================================================
|
|
354
|
+
// SKILL STORAGE
|
|
355
|
+
// ============================================================================
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Save skill to disk
|
|
359
|
+
* @param {Object} skill - Skill object
|
|
360
|
+
* @param {string} skillMd - SKILL.md content
|
|
361
|
+
*/
|
|
362
|
+
function saveSkill(skill, skillMd) {
|
|
363
|
+
try {
|
|
364
|
+
// Create directory
|
|
365
|
+
const skillDir = path.join(SKILLS_DIR, skill.id.toLowerCase());
|
|
366
|
+
if (!fs.existsSync(skillDir)) {
|
|
367
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Save SKILL.md
|
|
371
|
+
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), skillMd, 'utf8');
|
|
372
|
+
|
|
373
|
+
// Save skill.json
|
|
374
|
+
fs.writeFileSync(
|
|
375
|
+
path.join(skillDir, 'skill.json'),
|
|
376
|
+
JSON.stringify(skill, null, 2),
|
|
377
|
+
'utf8'
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
// Update registry
|
|
381
|
+
updateRegistry(skill);
|
|
382
|
+
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error('Error saving skill:', error.message);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Update skill registry
|
|
390
|
+
*/
|
|
391
|
+
function updateRegistry(skill) {
|
|
392
|
+
try {
|
|
393
|
+
if (!fs.existsSync(SKILLS_DIR)) {
|
|
394
|
+
fs.mkdirSync(SKILLS_DIR, { recursive: true });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
let registry = [];
|
|
398
|
+
if (fs.existsSync(SKILL_REGISTRY)) {
|
|
399
|
+
registry = JSON.parse(fs.readFileSync(SKILL_REGISTRY, 'utf8'));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const existingIndex = registry.findIndex(s => s.id === skill.id);
|
|
403
|
+
const entry = {
|
|
404
|
+
id: skill.id,
|
|
405
|
+
name: skill.name,
|
|
406
|
+
version: skill.version,
|
|
407
|
+
category: skill.detection.category,
|
|
408
|
+
confidence: skill.metrics.confidence,
|
|
409
|
+
state: skill.lifecycle.state,
|
|
410
|
+
updatedAt: skill.updatedAt
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
if (existingIndex >= 0) {
|
|
414
|
+
registry[existingIndex] = entry;
|
|
415
|
+
} else {
|
|
416
|
+
registry.push(entry);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
fs.writeFileSync(SKILL_REGISTRY, JSON.stringify(registry, null, 2), 'utf8');
|
|
420
|
+
} catch (error) {
|
|
421
|
+
console.error('Error updating registry:', error.message);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Load all auto-generated skills
|
|
427
|
+
* @returns {Array} - Array of skills
|
|
428
|
+
*/
|
|
429
|
+
export function loadAutoSkills() {
|
|
430
|
+
try {
|
|
431
|
+
if (!fs.existsSync(SKILL_REGISTRY)) return [];
|
|
432
|
+
return JSON.parse(fs.readFileSync(SKILL_REGISTRY, 'utf8'));
|
|
433
|
+
} catch {
|
|
434
|
+
return [];
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Load a specific skill
|
|
440
|
+
* @param {string} skillId - Skill ID
|
|
441
|
+
* @returns {Object|null} - Skill or null
|
|
442
|
+
*/
|
|
443
|
+
export function loadSkill(skillId) {
|
|
444
|
+
try {
|
|
445
|
+
const skillPath = path.join(SKILLS_DIR, skillId.toLowerCase(), 'skill.json');
|
|
446
|
+
if (!fs.existsSync(skillPath)) return null;
|
|
447
|
+
return JSON.parse(fs.readFileSync(skillPath, 'utf8'));
|
|
448
|
+
} catch {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// ============================================================================
|
|
454
|
+
// SKILL LIFECYCLE
|
|
455
|
+
// ============================================================================
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Evaluate skill effectiveness
|
|
459
|
+
* @param {string} skillId - Skill ID
|
|
460
|
+
* @param {Object} outcome - Task outcome where skill was applied
|
|
461
|
+
* @returns {Object} - Updated skill metrics
|
|
462
|
+
*/
|
|
463
|
+
export function evaluateSkill(skillId, outcome) {
|
|
464
|
+
const skill = loadSkill(skillId);
|
|
465
|
+
if (!skill) return null;
|
|
466
|
+
|
|
467
|
+
skill.metrics.timesApplied++;
|
|
468
|
+
|
|
469
|
+
if (outcome.success && outcome.skillHelped) {
|
|
470
|
+
skill.metrics.timesHelped++;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Recalculate effectiveness
|
|
474
|
+
skill.metrics.effectiveness = skill.metrics.timesApplied > 0
|
|
475
|
+
? skill.metrics.timesHelped / skill.metrics.timesApplied
|
|
476
|
+
: 0;
|
|
477
|
+
|
|
478
|
+
skill.lifecycle.evaluationCount++;
|
|
479
|
+
skill.updatedAt = new Date().toISOString();
|
|
480
|
+
|
|
481
|
+
// Check if skill should be demoted
|
|
482
|
+
if (skill.metrics.effectiveness < 0.3 && skill.metrics.timesApplied >= 10) {
|
|
483
|
+
skill.lifecycle.state = 'underperforming';
|
|
484
|
+
skill.lifecycle.stateChangedAt = new Date().toISOString();
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Update file
|
|
488
|
+
const skillDir = path.join(SKILLS_DIR, skillId.toLowerCase());
|
|
489
|
+
fs.writeFileSync(
|
|
490
|
+
path.join(skillDir, 'skill.json'),
|
|
491
|
+
JSON.stringify(skill, null, 2),
|
|
492
|
+
'utf8'
|
|
493
|
+
);
|
|
494
|
+
updateRegistry(skill);
|
|
495
|
+
|
|
496
|
+
return skill;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Prune underperforming skills
|
|
501
|
+
* @returns {Object} - Prune results
|
|
502
|
+
*/
|
|
503
|
+
export function pruneUnderperformingSkills() {
|
|
504
|
+
const skills = loadAutoSkills();
|
|
505
|
+
const pruned = [];
|
|
506
|
+
|
|
507
|
+
for (const skillEntry of skills) {
|
|
508
|
+
const skill = loadSkill(skillEntry.id);
|
|
509
|
+
if (!skill) continue;
|
|
510
|
+
|
|
511
|
+
// Prune if underperforming for too long
|
|
512
|
+
if (skill.lifecycle.state === 'underperforming' && skill.lifecycle.evaluationCount >= 20) {
|
|
513
|
+
skill.lifecycle.state = 'pruned';
|
|
514
|
+
skill.lifecycle.stateChangedAt = new Date().toISOString();
|
|
515
|
+
|
|
516
|
+
// Move to archive instead of delete
|
|
517
|
+
archiveSkill(skill);
|
|
518
|
+
pruned.push(skill.id);
|
|
519
|
+
|
|
520
|
+
recordSkillEvent({ type: 'pruned' });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return { pruned };
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Archive a skill (soft delete)
|
|
529
|
+
*/
|
|
530
|
+
function archiveSkill(skill) {
|
|
531
|
+
try {
|
|
532
|
+
const archiveDir = path.join(SKILLS_DIR, '_archive');
|
|
533
|
+
if (!fs.existsSync(archiveDir)) {
|
|
534
|
+
fs.mkdirSync(archiveDir, { recursive: true });
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const skillDir = path.join(SKILLS_DIR, skill.id.toLowerCase());
|
|
538
|
+
const archivePath = path.join(archiveDir, skill.id.toLowerCase());
|
|
539
|
+
|
|
540
|
+
if (fs.existsSync(skillDir)) {
|
|
541
|
+
fs.renameSync(skillDir, archivePath);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// Remove from registry
|
|
545
|
+
const registry = loadAutoSkills().filter(s => s.id !== skill.id);
|
|
546
|
+
fs.writeFileSync(SKILL_REGISTRY, JSON.stringify(registry, null, 2), 'utf8');
|
|
547
|
+
} catch (error) {
|
|
548
|
+
console.error('Error archiving skill:', error.message);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Get skill statistics
|
|
554
|
+
* @returns {Object} - Statistics
|
|
555
|
+
*/
|
|
556
|
+
export function getSkillStats() {
|
|
557
|
+
const skills = loadAutoSkills();
|
|
558
|
+
|
|
559
|
+
return {
|
|
560
|
+
total: skills.length,
|
|
561
|
+
active: skills.filter(s => s.state === 'active').length,
|
|
562
|
+
underperforming: skills.filter(s => s.state === 'underperforming').length,
|
|
563
|
+
avgConfidence: skills.length > 0
|
|
564
|
+
? skills.reduce((sum, s) => sum + s.confidence, 0) / skills.length
|
|
565
|
+
: 0,
|
|
566
|
+
categories: [...new Set(skills.map(s => s.category))]
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// ============================================================================
|
|
571
|
+
// EXPORTS
|
|
572
|
+
// ============================================================================
|
|
573
|
+
|
|
574
|
+
export default {
|
|
575
|
+
createContextFingerprint,
|
|
576
|
+
checkSkillReadiness,
|
|
577
|
+
generateSkill,
|
|
578
|
+
loadAutoSkills,
|
|
579
|
+
loadSkill,
|
|
580
|
+
evaluateSkill,
|
|
581
|
+
pruneUnderperformingSkills,
|
|
582
|
+
getSkillStats,
|
|
583
|
+
SKILL_REQUIREMENTS
|
|
584
|
+
};
|