pikakit 1.0.18 → 1.0.19
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.
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge Storage v6.0 - Unified Schema
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for all lessons in PikaKit AutoLearn system.
|
|
5
|
+
* Consolidates legacy lessons-learned.yaml, mistakes.yaml, and improvements.yaml
|
|
6
|
+
*
|
|
7
|
+
* @version 6.0.0
|
|
8
|
+
* @author PikaKit
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import fs from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import yaml from 'js-yaml';
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// CONFIGURATION
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
const KNOWLEDGE_DIR = path.join(process.cwd(), '.agent', 'knowledge');
|
|
20
|
+
const KNOWLEDGE_FILE = path.join(KNOWLEDGE_DIR, 'knowledge.yaml');
|
|
21
|
+
|
|
22
|
+
// Legacy files (for backward compatibility)
|
|
23
|
+
const LEGACY_FILES = {
|
|
24
|
+
lessonsLearned: path.join(KNOWLEDGE_DIR, 'lessons-learned.yaml'),
|
|
25
|
+
mistakes: path.join(KNOWLEDGE_DIR, 'mistakes.yaml'),
|
|
26
|
+
improvements: path.join(KNOWLEDGE_DIR, 'improvements.yaml')
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// SCHEMA DEFINITION
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {Object} Lesson
|
|
35
|
+
* @property {string} id - Unique identifier (LESSON-XXX)
|
|
36
|
+
* @property {string} type - Type: 'mistake' | 'improvement' | 'pattern'
|
|
37
|
+
* @property {string} pattern - Regex pattern to match
|
|
38
|
+
* @property {string} message - Human-readable explanation
|
|
39
|
+
* @property {string} severity - 'ERROR' | 'WARNING' | 'INFO'
|
|
40
|
+
* @property {string} intent - 'prevent' | 'optimize' | 'inform'
|
|
41
|
+
* @property {number} confidence - 0.0 to 1.0
|
|
42
|
+
* @property {string} maturity - 'learning' | 'stable'
|
|
43
|
+
* @property {number} hitCount - Number of times pattern was matched
|
|
44
|
+
* @property {string|null} lastHit - ISO8601 timestamp of last match
|
|
45
|
+
* @property {string[]} excludePaths - Glob patterns for excluded paths
|
|
46
|
+
* @property {string[]} tags - Category tags
|
|
47
|
+
* @property {Object|null} autoFix - Auto-fix configuration
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {Object} KnowledgeBase
|
|
52
|
+
* @property {number} version - Schema version (6.0)
|
|
53
|
+
* @property {string} createdAt - ISO8601 creation timestamp
|
|
54
|
+
* @property {string} updatedAt - ISO8601 last update timestamp
|
|
55
|
+
* @property {Lesson[]} lessons - Array of lessons
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create empty knowledge base with v6 schema
|
|
60
|
+
* @returns {KnowledgeBase}
|
|
61
|
+
*/
|
|
62
|
+
export function createEmptyKnowledge() {
|
|
63
|
+
return {
|
|
64
|
+
version: 6.0,
|
|
65
|
+
createdAt: new Date().toISOString(),
|
|
66
|
+
updatedAt: new Date().toISOString(),
|
|
67
|
+
lessons: []
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// LOADING
|
|
73
|
+
// ============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Load knowledge base with automatic version detection
|
|
77
|
+
* Priority: v6 (knowledge.yaml) > v4 (mistakes + improvements) > v3 (lessons-learned)
|
|
78
|
+
*
|
|
79
|
+
* @returns {KnowledgeBase}
|
|
80
|
+
*/
|
|
81
|
+
export function loadKnowledge() {
|
|
82
|
+
try {
|
|
83
|
+
// Priority 1: v6 unified format
|
|
84
|
+
if (fs.existsSync(KNOWLEDGE_FILE)) {
|
|
85
|
+
const content = fs.readFileSync(KNOWLEDGE_FILE, 'utf8');
|
|
86
|
+
const data = yaml.load(content) || createEmptyKnowledge();
|
|
87
|
+
return data;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Priority 2: v4 cognitive format (mistakes + improvements)
|
|
91
|
+
const hasV4 = fs.existsSync(LEGACY_FILES.mistakes) ||
|
|
92
|
+
fs.existsSync(LEGACY_FILES.improvements);
|
|
93
|
+
|
|
94
|
+
if (hasV4) {
|
|
95
|
+
return loadV4Legacy();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Priority 3: v3 legacy format
|
|
99
|
+
if (fs.existsSync(LEGACY_FILES.lessonsLearned)) {
|
|
100
|
+
return loadV3Legacy();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// No data - return empty
|
|
104
|
+
return createEmptyKnowledge();
|
|
105
|
+
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('[knowledge] Error loading:', error.message);
|
|
108
|
+
return createEmptyKnowledge();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Load from v4 legacy files (mistakes.yaml + improvements.yaml)
|
|
114
|
+
* @returns {KnowledgeBase}
|
|
115
|
+
*/
|
|
116
|
+
function loadV4Legacy() {
|
|
117
|
+
const knowledge = createEmptyKnowledge();
|
|
118
|
+
|
|
119
|
+
// Load mistakes
|
|
120
|
+
if (fs.existsSync(LEGACY_FILES.mistakes)) {
|
|
121
|
+
try {
|
|
122
|
+
const data = yaml.load(fs.readFileSync(LEGACY_FILES.mistakes, 'utf8'));
|
|
123
|
+
const mistakes = (data.mistakes || []).map(m => ({
|
|
124
|
+
id: m.id,
|
|
125
|
+
type: 'mistake',
|
|
126
|
+
pattern: m.pattern,
|
|
127
|
+
message: m.message,
|
|
128
|
+
severity: m.severity || 'WARNING',
|
|
129
|
+
intent: 'prevent',
|
|
130
|
+
confidence: m.cognitive?.confidence || 0.5,
|
|
131
|
+
maturity: m.cognitive?.maturity || 'learning',
|
|
132
|
+
hitCount: m.hitCount || 0,
|
|
133
|
+
lastHit: m.lastHit || null,
|
|
134
|
+
excludePaths: m.excludePaths || [],
|
|
135
|
+
tags: m.tags || [],
|
|
136
|
+
autoFix: m.autoFix || null
|
|
137
|
+
}));
|
|
138
|
+
knowledge.lessons.push(...mistakes);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
console.error('[knowledge] Error loading mistakes.yaml:', e.message);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Load improvements
|
|
145
|
+
if (fs.existsSync(LEGACY_FILES.improvements)) {
|
|
146
|
+
try {
|
|
147
|
+
const data = yaml.load(fs.readFileSync(LEGACY_FILES.improvements, 'utf8'));
|
|
148
|
+
const improvements = (data.improvements || []).map(i => ({
|
|
149
|
+
id: i.id,
|
|
150
|
+
type: 'improvement',
|
|
151
|
+
pattern: i.pattern,
|
|
152
|
+
message: i.message,
|
|
153
|
+
severity: 'INFO',
|
|
154
|
+
intent: 'optimize',
|
|
155
|
+
confidence: i.cognitive?.confidence || 0.5,
|
|
156
|
+
maturity: i.cognitive?.maturity || 'learning',
|
|
157
|
+
hitCount: i.hitCount || i.appliedCount || 0,
|
|
158
|
+
lastHit: i.lastHit || i.lastApplied || null,
|
|
159
|
+
excludePaths: i.excludePaths || [],
|
|
160
|
+
tags: i.tags || [],
|
|
161
|
+
autoFix: null
|
|
162
|
+
}));
|
|
163
|
+
knowledge.lessons.push(...improvements);
|
|
164
|
+
} catch (e) {
|
|
165
|
+
console.error('[knowledge] Error loading improvements.yaml:', e.message);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return knowledge;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Load from v3 legacy file (lessons-learned.yaml)
|
|
174
|
+
* @returns {KnowledgeBase}
|
|
175
|
+
*/
|
|
176
|
+
function loadV3Legacy() {
|
|
177
|
+
const knowledge = createEmptyKnowledge();
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const data = yaml.load(fs.readFileSync(LEGACY_FILES.lessonsLearned, 'utf8'));
|
|
181
|
+
const lessons = (data.lessons || []).map(l => ({
|
|
182
|
+
id: l.id,
|
|
183
|
+
type: l.severity === 'ERROR' ? 'mistake' : 'pattern',
|
|
184
|
+
pattern: l.pattern,
|
|
185
|
+
message: l.message,
|
|
186
|
+
severity: l.severity || 'WARNING',
|
|
187
|
+
intent: l.severity === 'ERROR' ? 'prevent' : 'inform',
|
|
188
|
+
confidence: l.hitCount > 10 ? 0.9 : 0.5,
|
|
189
|
+
maturity: l.hitCount > 10 ? 'stable' : 'learning',
|
|
190
|
+
hitCount: l.hitCount || 0,
|
|
191
|
+
lastHit: l.lastHit || null,
|
|
192
|
+
excludePaths: [],
|
|
193
|
+
tags: [l.category || 'general'],
|
|
194
|
+
autoFix: null
|
|
195
|
+
}));
|
|
196
|
+
knowledge.lessons.push(...lessons);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
console.error('[knowledge] Error loading lessons-learned.yaml:', e.message);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return knowledge;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// SAVING
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Save knowledge base to unified format
|
|
210
|
+
* @param {KnowledgeBase} knowledge
|
|
211
|
+
*/
|
|
212
|
+
export function saveKnowledge(knowledge) {
|
|
213
|
+
try {
|
|
214
|
+
// Ensure directory exists
|
|
215
|
+
if (!fs.existsSync(KNOWLEDGE_DIR)) {
|
|
216
|
+
fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Update timestamp
|
|
220
|
+
knowledge.updatedAt = new Date().toISOString();
|
|
221
|
+
knowledge.version = 6.0;
|
|
222
|
+
|
|
223
|
+
// Write to file
|
|
224
|
+
const yamlContent = yaml.dump(knowledge, {
|
|
225
|
+
lineWidth: -1,
|
|
226
|
+
quotingType: '"',
|
|
227
|
+
forceQuotes: false
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
fs.writeFileSync(KNOWLEDGE_FILE, yamlContent, 'utf8');
|
|
231
|
+
|
|
232
|
+
return true;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error('[knowledge] Error saving:', error.message);
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ============================================================================
|
|
240
|
+
// OPERATIONS
|
|
241
|
+
// ============================================================================
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Add a new lesson
|
|
245
|
+
* @param {Lesson} lesson
|
|
246
|
+
* @returns {boolean}
|
|
247
|
+
*/
|
|
248
|
+
export function addLesson(lesson) {
|
|
249
|
+
const knowledge = loadKnowledge();
|
|
250
|
+
|
|
251
|
+
// Check for duplicate ID
|
|
252
|
+
if (knowledge.lessons.some(l => l.id === lesson.id)) {
|
|
253
|
+
console.warn(`[knowledge] Lesson ${lesson.id} already exists`);
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
knowledge.lessons.push(lesson);
|
|
258
|
+
return saveKnowledge(knowledge);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Update an existing lesson
|
|
263
|
+
* @param {string} id
|
|
264
|
+
* @param {Partial<Lesson>} updates
|
|
265
|
+
* @returns {boolean}
|
|
266
|
+
*/
|
|
267
|
+
export function updateLesson(id, updates) {
|
|
268
|
+
const knowledge = loadKnowledge();
|
|
269
|
+
const index = knowledge.lessons.findIndex(l => l.id === id);
|
|
270
|
+
|
|
271
|
+
if (index === -1) {
|
|
272
|
+
console.warn(`[knowledge] Lesson ${id} not found`);
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
knowledge.lessons[index] = { ...knowledge.lessons[index], ...updates };
|
|
277
|
+
return saveKnowledge(knowledge);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Remove a lesson
|
|
282
|
+
* @param {string} id
|
|
283
|
+
* @returns {boolean}
|
|
284
|
+
*/
|
|
285
|
+
export function removeLesson(id) {
|
|
286
|
+
const knowledge = loadKnowledge();
|
|
287
|
+
const initialLength = knowledge.lessons.length;
|
|
288
|
+
knowledge.lessons = knowledge.lessons.filter(l => l.id !== id);
|
|
289
|
+
|
|
290
|
+
if (knowledge.lessons.length === initialLength) {
|
|
291
|
+
console.warn(`[knowledge] Lesson ${id} not found`);
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return saveKnowledge(knowledge);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Get lessons by type
|
|
300
|
+
* @param {'mistake' | 'improvement' | 'pattern'} type
|
|
301
|
+
* @returns {Lesson[]}
|
|
302
|
+
*/
|
|
303
|
+
export function getLessonsByType(type) {
|
|
304
|
+
const knowledge = loadKnowledge();
|
|
305
|
+
return knowledge.lessons.filter(l => l.type === type);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get total lesson count
|
|
310
|
+
* @returns {number}
|
|
311
|
+
*/
|
|
312
|
+
export function getLessonCount() {
|
|
313
|
+
const knowledge = loadKnowledge();
|
|
314
|
+
return knowledge.lessons.length;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Record a hit on a lesson
|
|
319
|
+
* @param {string} id
|
|
320
|
+
* @returns {boolean}
|
|
321
|
+
*/
|
|
322
|
+
export function recordHit(id) {
|
|
323
|
+
const knowledge = loadKnowledge();
|
|
324
|
+
const lesson = knowledge.lessons.find(l => l.id === id);
|
|
325
|
+
|
|
326
|
+
if (!lesson) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
lesson.hitCount = (lesson.hitCount || 0) + 1;
|
|
331
|
+
lesson.lastHit = new Date().toISOString();
|
|
332
|
+
|
|
333
|
+
// Auto-update maturity based on hits
|
|
334
|
+
if (lesson.hitCount >= 10) {
|
|
335
|
+
lesson.maturity = 'stable';
|
|
336
|
+
lesson.confidence = Math.min(0.95, lesson.confidence + 0.05);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return saveKnowledge(knowledge);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ============================================================================
|
|
343
|
+
// EXPORTS
|
|
344
|
+
// ============================================================================
|
|
345
|
+
|
|
346
|
+
export default {
|
|
347
|
+
loadKnowledge,
|
|
348
|
+
saveKnowledge,
|
|
349
|
+
addLesson,
|
|
350
|
+
updateLesson,
|
|
351
|
+
removeLesson,
|
|
352
|
+
getLessonsByType,
|
|
353
|
+
getLessonCount,
|
|
354
|
+
recordHit,
|
|
355
|
+
createEmptyKnowledge,
|
|
356
|
+
KNOWLEDGE_FILE,
|
|
357
|
+
LEGACY_FILES
|
|
358
|
+
};
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Migration Script: v3/v4 → v6
|
|
4
|
+
*
|
|
5
|
+
* Migrates legacy knowledge files to unified knowledge.yaml format.
|
|
6
|
+
*
|
|
7
|
+
* Source files:
|
|
8
|
+
* - lessons-learned.yaml (v3)
|
|
9
|
+
* - mistakes.yaml (v4)
|
|
10
|
+
* - improvements.yaml (v4)
|
|
11
|
+
*
|
|
12
|
+
* Target:
|
|
13
|
+
* - knowledge.yaml (v6)
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* node migrate-to-v6.js
|
|
17
|
+
* node migrate-to-v6.js --dry-run
|
|
18
|
+
*
|
|
19
|
+
* @version 6.0.0
|
|
20
|
+
* @author PikaKit
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import fs from 'fs';
|
|
24
|
+
import path from 'path';
|
|
25
|
+
import yaml from 'js-yaml';
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// CONFIGURATION
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
const KNOWLEDGE_DIR = path.join(process.cwd(), '.agent', 'knowledge');
|
|
32
|
+
const BACKUP_DIR = path.join(KNOWLEDGE_DIR, 'backup');
|
|
33
|
+
|
|
34
|
+
const FILES = {
|
|
35
|
+
lessonsLearned: path.join(KNOWLEDGE_DIR, 'lessons-learned.yaml'),
|
|
36
|
+
mistakes: path.join(KNOWLEDGE_DIR, 'mistakes.yaml'),
|
|
37
|
+
improvements: path.join(KNOWLEDGE_DIR, 'improvements.yaml'),
|
|
38
|
+
knowledge: path.join(KNOWLEDGE_DIR, 'knowledge.yaml')
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const isDryRun = process.argv.includes('--dry-run');
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// LOGGING
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
const log = {
|
|
48
|
+
info: (msg) => console.log(` ℹ️ ${msg}`),
|
|
49
|
+
success: (msg) => console.log(` ✅ ${msg}`),
|
|
50
|
+
warning: (msg) => console.log(` ⚠️ ${msg}`),
|
|
51
|
+
error: (msg) => console.log(` ❌ ${msg}`),
|
|
52
|
+
step: (n, msg) => console.log(`\n📌 Step ${n}: ${msg}`)
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// BACKUP
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
function backupFiles() {
|
|
60
|
+
log.step(1, 'Backing up existing files');
|
|
61
|
+
|
|
62
|
+
if (!fs.existsSync(BACKUP_DIR)) {
|
|
63
|
+
fs.mkdirSync(BACKUP_DIR, { recursive: true });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
67
|
+
let backedUp = 0;
|
|
68
|
+
|
|
69
|
+
for (const [name, filepath] of Object.entries(FILES)) {
|
|
70
|
+
if (name === 'knowledge') continue; // Don't backup target
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(filepath)) {
|
|
73
|
+
const backupPath = path.join(BACKUP_DIR, `${path.basename(filepath)}.${timestamp}.bak`);
|
|
74
|
+
|
|
75
|
+
if (!isDryRun) {
|
|
76
|
+
fs.copyFileSync(filepath, backupPath);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
log.success(`Backed up ${path.basename(filepath)}`);
|
|
80
|
+
backedUp++;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (backedUp === 0) {
|
|
85
|
+
log.warning('No files to backup');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// LOADING LEGACY DATA
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
function loadLegacyData() {
|
|
96
|
+
log.step(2, 'Loading legacy data');
|
|
97
|
+
|
|
98
|
+
const lessons = [];
|
|
99
|
+
const stats = { v3: 0, v4Mistakes: 0, v4Improvements: 0 };
|
|
100
|
+
|
|
101
|
+
// Load v3 lessons-learned.yaml
|
|
102
|
+
if (fs.existsSync(FILES.lessonsLearned)) {
|
|
103
|
+
try {
|
|
104
|
+
const data = yaml.load(fs.readFileSync(FILES.lessonsLearned, 'utf8'));
|
|
105
|
+
const v3Lessons = (data.lessons || []).map(l => ({
|
|
106
|
+
id: l.id,
|
|
107
|
+
type: l.severity === 'ERROR' ? 'mistake' : 'pattern',
|
|
108
|
+
pattern: l.pattern,
|
|
109
|
+
message: l.message,
|
|
110
|
+
severity: l.severity || 'WARNING',
|
|
111
|
+
intent: l.severity === 'ERROR' ? 'prevent' : 'inform',
|
|
112
|
+
confidence: l.hitCount > 10 ? 0.9 : 0.5,
|
|
113
|
+
maturity: l.hitCount > 10 ? 'stable' : 'learning',
|
|
114
|
+
hitCount: l.hitCount || 0,
|
|
115
|
+
lastHit: l.lastHit || null,
|
|
116
|
+
excludePaths: [],
|
|
117
|
+
tags: [l.category || 'general'],
|
|
118
|
+
autoFix: null,
|
|
119
|
+
_source: 'lessons-learned.yaml'
|
|
120
|
+
}));
|
|
121
|
+
lessons.push(...v3Lessons);
|
|
122
|
+
stats.v3 = v3Lessons.length;
|
|
123
|
+
log.success(`Loaded ${v3Lessons.length} lesson(s) from lessons-learned.yaml`);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
log.error(`Failed to load lessons-learned.yaml: ${e.message}`);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
log.info('lessons-learned.yaml not found, skipping');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Load v4 mistakes.yaml
|
|
132
|
+
if (fs.existsSync(FILES.mistakes)) {
|
|
133
|
+
try {
|
|
134
|
+
const data = yaml.load(fs.readFileSync(FILES.mistakes, 'utf8'));
|
|
135
|
+
const mistakes = (data.mistakes || []).map(m => ({
|
|
136
|
+
id: m.id,
|
|
137
|
+
type: 'mistake',
|
|
138
|
+
pattern: m.pattern,
|
|
139
|
+
message: m.message,
|
|
140
|
+
severity: m.severity || 'WARNING',
|
|
141
|
+
intent: 'prevent',
|
|
142
|
+
confidence: m.cognitive?.confidence || 0.5,
|
|
143
|
+
maturity: m.cognitive?.maturity || 'learning',
|
|
144
|
+
hitCount: m.hitCount || 0,
|
|
145
|
+
lastHit: m.lastHit || null,
|
|
146
|
+
excludePaths: m.excludePaths || [],
|
|
147
|
+
tags: m.tags || [],
|
|
148
|
+
autoFix: m.autoFix || null,
|
|
149
|
+
_source: 'mistakes.yaml'
|
|
150
|
+
}));
|
|
151
|
+
lessons.push(...mistakes);
|
|
152
|
+
stats.v4Mistakes = mistakes.length;
|
|
153
|
+
log.success(`Loaded ${mistakes.length} mistake(s) from mistakes.yaml`);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
log.error(`Failed to load mistakes.yaml: ${e.message}`);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
log.info('mistakes.yaml not found, skipping');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Load v4 improvements.yaml
|
|
162
|
+
if (fs.existsSync(FILES.improvements)) {
|
|
163
|
+
try {
|
|
164
|
+
const data = yaml.load(fs.readFileSync(FILES.improvements, 'utf8'));
|
|
165
|
+
const improvements = (data.improvements || []).map(i => ({
|
|
166
|
+
id: i.id,
|
|
167
|
+
type: 'improvement',
|
|
168
|
+
pattern: i.pattern,
|
|
169
|
+
message: i.message,
|
|
170
|
+
severity: 'INFO',
|
|
171
|
+
intent: 'optimize',
|
|
172
|
+
confidence: i.cognitive?.confidence || 0.5,
|
|
173
|
+
maturity: i.cognitive?.maturity || 'learning',
|
|
174
|
+
hitCount: i.hitCount || i.appliedCount || 0,
|
|
175
|
+
lastHit: i.lastHit || i.lastApplied || null,
|
|
176
|
+
excludePaths: i.excludePaths || [],
|
|
177
|
+
tags: i.tags || [],
|
|
178
|
+
autoFix: null,
|
|
179
|
+
_source: 'improvements.yaml'
|
|
180
|
+
}));
|
|
181
|
+
lessons.push(...improvements);
|
|
182
|
+
stats.v4Improvements = improvements.length;
|
|
183
|
+
log.success(`Loaded ${improvements.length} improvement(s) from improvements.yaml`);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
log.error(`Failed to load improvements.yaml: ${e.message}`);
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
log.info('improvements.yaml not found, skipping');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { lessons, stats };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// DEDUPLICATION
|
|
196
|
+
// ============================================================================
|
|
197
|
+
|
|
198
|
+
function deduplicateLessons(lessons) {
|
|
199
|
+
log.step(3, 'Deduplicating lessons');
|
|
200
|
+
|
|
201
|
+
const seen = new Map();
|
|
202
|
+
const duplicates = [];
|
|
203
|
+
|
|
204
|
+
for (const lesson of lessons) {
|
|
205
|
+
// Check by pattern (primary dedup key)
|
|
206
|
+
if (seen.has(lesson.pattern)) {
|
|
207
|
+
const existing = seen.get(lesson.pattern);
|
|
208
|
+
duplicates.push({
|
|
209
|
+
kept: existing.id,
|
|
210
|
+
removed: lesson.id,
|
|
211
|
+
pattern: lesson.pattern
|
|
212
|
+
});
|
|
213
|
+
// Keep the one with more hits
|
|
214
|
+
if (lesson.hitCount > existing.hitCount) {
|
|
215
|
+
seen.set(lesson.pattern, lesson);
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
seen.set(lesson.pattern, lesson);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const deduped = Array.from(seen.values());
|
|
223
|
+
|
|
224
|
+
if (duplicates.length > 0) {
|
|
225
|
+
log.warning(`Found ${duplicates.length} duplicate(s), keeping highest hitCount`);
|
|
226
|
+
duplicates.forEach(d => {
|
|
227
|
+
log.info(` Merged ${d.removed} into ${d.kept}`);
|
|
228
|
+
});
|
|
229
|
+
} else {
|
|
230
|
+
log.success('No duplicates found');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return deduped;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// RENUMBER IDS
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
240
|
+
function renumberLessons(lessons) {
|
|
241
|
+
log.step(4, 'Renumbering lesson IDs');
|
|
242
|
+
|
|
243
|
+
return lessons.map((lesson, index) => {
|
|
244
|
+
// Remove internal _source field
|
|
245
|
+
const { _source, ...cleanLesson } = lesson;
|
|
246
|
+
|
|
247
|
+
// Assign new sequential ID
|
|
248
|
+
cleanLesson.id = `LESSON-${String(index + 1).padStart(3, '0')}`;
|
|
249
|
+
|
|
250
|
+
return cleanLesson;
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ============================================================================
|
|
255
|
+
// SAVE
|
|
256
|
+
// ============================================================================
|
|
257
|
+
|
|
258
|
+
function saveKnowledge(lessons) {
|
|
259
|
+
log.step(5, 'Saving unified knowledge.yaml');
|
|
260
|
+
|
|
261
|
+
const knowledge = {
|
|
262
|
+
version: 6.0,
|
|
263
|
+
createdAt: new Date().toISOString(),
|
|
264
|
+
updatedAt: new Date().toISOString(),
|
|
265
|
+
lessons
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const yamlContent = yaml.dump(knowledge, {
|
|
269
|
+
lineWidth: -1,
|
|
270
|
+
quotingType: '"',
|
|
271
|
+
forceQuotes: false
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (isDryRun) {
|
|
275
|
+
log.info('[DRY RUN] Would write to knowledge.yaml');
|
|
276
|
+
console.log('\n--- Preview of knowledge.yaml ---');
|
|
277
|
+
console.log(yamlContent.slice(0, 1000) + (yamlContent.length > 1000 ? '\n...' : ''));
|
|
278
|
+
} else {
|
|
279
|
+
fs.writeFileSync(FILES.knowledge, yamlContent, 'utf8');
|
|
280
|
+
log.success(`Saved ${lessons.length} lesson(s) to knowledge.yaml`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ============================================================================
|
|
287
|
+
// VERIFY
|
|
288
|
+
// ============================================================================
|
|
289
|
+
|
|
290
|
+
function verifyMigration(expectedCount) {
|
|
291
|
+
log.step(6, 'Verifying migration');
|
|
292
|
+
|
|
293
|
+
if (isDryRun) {
|
|
294
|
+
log.info('[DRY RUN] Skipping verification');
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!fs.existsSync(FILES.knowledge)) {
|
|
299
|
+
log.error('knowledge.yaml not created');
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const data = yaml.load(fs.readFileSync(FILES.knowledge, 'utf8'));
|
|
304
|
+
const actualCount = data.lessons?.length || 0;
|
|
305
|
+
|
|
306
|
+
if (actualCount !== expectedCount) {
|
|
307
|
+
log.error(`Count mismatch: expected ${expectedCount}, got ${actualCount}`);
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
log.success(`Verified: ${actualCount} lessons in knowledge.yaml`);
|
|
312
|
+
log.success(`Version: ${data.version}`);
|
|
313
|
+
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// MAIN
|
|
319
|
+
// ============================================================================
|
|
320
|
+
|
|
321
|
+
async function main() {
|
|
322
|
+
console.log('\n🔄 Knowledge Storage Migration: v3/v4 → v6');
|
|
323
|
+
console.log('='.repeat(50));
|
|
324
|
+
|
|
325
|
+
if (isDryRun) {
|
|
326
|
+
console.log('🔍 DRY RUN MODE - No changes will be made\n');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Step 1: Backup
|
|
330
|
+
backupFiles();
|
|
331
|
+
|
|
332
|
+
// Step 2: Load legacy data
|
|
333
|
+
const { lessons, stats } = loadLegacyData();
|
|
334
|
+
|
|
335
|
+
if (lessons.length === 0) {
|
|
336
|
+
log.warning('No lessons found to migrate');
|
|
337
|
+
console.log('\n✅ Migration complete (nothing to migrate)');
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Step 3: Deduplicate
|
|
342
|
+
const deduped = deduplicateLessons(lessons);
|
|
343
|
+
|
|
344
|
+
// Step 4: Renumber
|
|
345
|
+
const renumbered = renumberLessons(deduped);
|
|
346
|
+
|
|
347
|
+
// Step 5: Save
|
|
348
|
+
saveKnowledge(renumbered);
|
|
349
|
+
|
|
350
|
+
// Step 6: Verify
|
|
351
|
+
const verified = verifyMigration(renumbered.length);
|
|
352
|
+
|
|
353
|
+
// Summary
|
|
354
|
+
console.log('\n' + '='.repeat(50));
|
|
355
|
+
console.log('📊 Migration Summary');
|
|
356
|
+
console.log('='.repeat(50));
|
|
357
|
+
console.log(` Source files:`);
|
|
358
|
+
console.log(` - lessons-learned.yaml: ${stats.v3} lesson(s)`);
|
|
359
|
+
console.log(` - mistakes.yaml: ${stats.v4Mistakes} mistake(s)`);
|
|
360
|
+
console.log(` - improvements.yaml: ${stats.v4Improvements} improvement(s)`);
|
|
361
|
+
console.log(` Total loaded: ${lessons.length}`);
|
|
362
|
+
console.log(` After dedup: ${deduped.length}`);
|
|
363
|
+
console.log(` Final count: ${renumbered.length}`);
|
|
364
|
+
console.log(` Verified: ${verified ? '✅ Yes' : '❌ No'}`);
|
|
365
|
+
|
|
366
|
+
if (isDryRun) {
|
|
367
|
+
console.log('\n🔍 This was a dry run. Run without --dry-run to apply changes.');
|
|
368
|
+
} else {
|
|
369
|
+
console.log('\n✅ Migration complete!');
|
|
370
|
+
console.log('📁 Backup files saved to: .agent/knowledge/backup/');
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
main().catch(e => {
|
|
375
|
+
log.error(`Migration failed: ${e.message}`);
|
|
376
|
+
process.exit(1);
|
|
377
|
+
});
|
|
@@ -23,6 +23,7 @@ import pretty from "./ui/pretty.js";
|
|
|
23
23
|
import * as p from "@clack/prompts";
|
|
24
24
|
import pc from "picocolors";
|
|
25
25
|
import { checkEvolutionThreshold, queueEvolutionSignal } from "./evolution-signal.js";
|
|
26
|
+
import { loadKnowledge as loadKnowledgeV6, saveKnowledge as saveKnowledgeV6 } from "./knowledge.js";
|
|
26
27
|
|
|
27
28
|
// ============================================================================
|
|
28
29
|
// PERCEPTION LAYER - Cognitive Enhancements
|
|
@@ -142,66 +143,25 @@ function createCognitiveLesson(rawLesson) {
|
|
|
142
143
|
// ============================================================================
|
|
143
144
|
|
|
144
145
|
/**
|
|
145
|
-
* Load knowledge base from
|
|
146
|
-
*
|
|
146
|
+
* Load knowledge base from unified v6 format
|
|
147
|
+
* With backward compatibility for v3.x and v4.x
|
|
147
148
|
* @returns {{ lessons: Array<{ id: string, pattern: string, message: string, severity: string, hitCount?: number, lastHit?: string }>, version?: number }}
|
|
148
149
|
*/
|
|
149
150
|
export function loadKnowledge() {
|
|
150
151
|
try {
|
|
151
|
-
//
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const improvements = fs.existsSync(improvementsPath)
|
|
163
|
-
? yaml.load(fs.readFileSync(improvementsPath, 'utf8'))
|
|
164
|
-
: { improvements: [] };
|
|
165
|
-
|
|
166
|
-
// Flatten and apply cognitive transformation
|
|
167
|
-
const rawLessons = [
|
|
168
|
-
...(mistakes.mistakes || []).map(m => ({
|
|
169
|
-
id: m.id,
|
|
170
|
-
pattern: m.pattern,
|
|
171
|
-
message: m.message,
|
|
172
|
-
severity: m.severity || 'WARNING',
|
|
173
|
-
hitCount: m.hitCount || 0,
|
|
174
|
-
lastHit: m.lastHit,
|
|
175
|
-
excludePaths: m.excludePaths,
|
|
176
|
-
tags: m.tags,
|
|
177
|
-
autoFix: m.autoFix, // Preserve autoFix for Fix All
|
|
178
|
-
type: 'mistake', // Mark for tracking
|
|
179
|
-
})),
|
|
180
|
-
...(improvements.improvements || []).map(i => ({
|
|
181
|
-
id: i.id,
|
|
182
|
-
pattern: i.pattern,
|
|
183
|
-
message: i.message,
|
|
184
|
-
severity: 'INFO', // Improvements are informational
|
|
185
|
-
hitCount: i.appliedCount || 0,
|
|
186
|
-
lastHit: i.lastApplied,
|
|
187
|
-
excludePaths: i.excludePaths, // PRESERVE excludePaths!
|
|
188
|
-
tags: i.tags,
|
|
189
|
-
type: 'improvement', // Mark for tracking
|
|
190
|
-
}))
|
|
191
|
-
];
|
|
192
|
-
|
|
193
|
-
// Apply cognitive transformation to create agent-level lessons
|
|
194
|
-
const lessons = rawLessons.map(createCognitiveLesson);
|
|
195
|
-
|
|
196
|
-
return { lessons, version: 4.0 };
|
|
197
|
-
}
|
|
152
|
+
// Use unified v6 loader (handles all versions internally)
|
|
153
|
+
const knowledge = loadKnowledgeV6();
|
|
154
|
+
|
|
155
|
+
// Transform lessons for cognitive processing
|
|
156
|
+
const lessons = knowledge.lessons.map(lesson => {
|
|
157
|
+
// Apply cognitive transformation if not already present
|
|
158
|
+
if (!lesson.cognitive) {
|
|
159
|
+
return createCognitiveLesson(lesson);
|
|
160
|
+
}
|
|
161
|
+
return lesson;
|
|
162
|
+
});
|
|
198
163
|
|
|
199
|
-
|
|
200
|
-
if (!fs.existsSync(LESSONS_PATH)) {
|
|
201
|
-
return { lessons: [], version: 1 };
|
|
202
|
-
}
|
|
203
|
-
const content = fs.readFileSync(LESSONS_PATH, "utf8");
|
|
204
|
-
return yaml.load(content) || { lessons: [], version: 1 };
|
|
164
|
+
return { lessons, version: knowledge.version || 6.0 };
|
|
205
165
|
} catch (error) {
|
|
206
166
|
if (DEBUG) console.error("Error loading knowledge:", error.message);
|
|
207
167
|
return { lessons: [], version: 1 };
|
|
@@ -209,48 +169,20 @@ export function loadKnowledge() {
|
|
|
209
169
|
}
|
|
210
170
|
|
|
211
171
|
/**
|
|
212
|
-
* Save knowledge base to
|
|
213
|
-
* Supports both v3.x and v4.x formats
|
|
172
|
+
* Save knowledge base to unified v6 format
|
|
214
173
|
* @param {{ lessons: Array, version?: number }} data
|
|
215
174
|
*/
|
|
216
175
|
export function saveKnowledge(data) {
|
|
217
176
|
try {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
});
|
|
226
|
-
const improvements = data.lessons.filter(l => l.type === 'improvement').map(i => {
|
|
227
|
-
const { type, severity, ...rest } = i; // Remove type and severity
|
|
228
|
-
return {
|
|
229
|
-
...rest,
|
|
230
|
-
excludePaths: i.excludePaths || [], // PRESERVE excludePaths explicitly
|
|
231
|
-
appliedCount: rest.hitCount,
|
|
232
|
-
lastApplied: rest.lastHit,
|
|
233
|
-
};
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// Save mistakes
|
|
237
|
-
if (mistakes.length > 0) {
|
|
238
|
-
const mistakesPath = path.join(KNOWLEDGE_DIR, 'mistakes.yaml');
|
|
239
|
-
const mistakesData = yaml.dump({ version: 4.0, mistakes }, { lineWidth: -1 });
|
|
240
|
-
fs.writeFileSync(mistakesPath, mistakesData, 'utf8');
|
|
241
|
-
}
|
|
177
|
+
// Convert to v6 format
|
|
178
|
+
const knowledge = {
|
|
179
|
+
version: 6.0,
|
|
180
|
+
createdAt: data.createdAt || new Date().toISOString(),
|
|
181
|
+
updatedAt: new Date().toISOString(),
|
|
182
|
+
lessons: data.lessons
|
|
183
|
+
};
|
|
242
184
|
|
|
243
|
-
|
|
244
|
-
if (improvements.length > 0) {
|
|
245
|
-
const improvementsPath = path.join(KNOWLEDGE_DIR, 'improvements.yaml');
|
|
246
|
-
const improvementsData = yaml.dump({ version: 4.0, improvements }, { lineWidth: -1 });
|
|
247
|
-
fs.writeFileSync(improvementsPath, improvementsData, 'utf8');
|
|
248
|
-
}
|
|
249
|
-
} else {
|
|
250
|
-
// v3.x: Save to lessons-learned.yaml
|
|
251
|
-
const str = yaml.dump(data, { lineWidth: -1 });
|
|
252
|
-
fs.writeFileSync(LESSONS_PATH, str, "utf8");
|
|
253
|
-
}
|
|
185
|
+
saveKnowledgeV6(knowledge);
|
|
254
186
|
} catch (error) {
|
|
255
187
|
console.error("❌ Failed to save knowledge base:", error.message);
|
|
256
188
|
}
|
|
@@ -42,8 +42,21 @@ const projectRoot = findProjectRoot();
|
|
|
42
42
|
const dashboardPath = path.join(__dirname, '..', 'dashboard');
|
|
43
43
|
const knowledgeDir = path.join(projectRoot, '.agent', 'knowledge');
|
|
44
44
|
|
|
45
|
-
// Read lessons count
|
|
45
|
+
// Read lessons count - prioritize v6 format
|
|
46
46
|
function getLessonsCount() {
|
|
47
|
+
// Priority 1: v6 unified format
|
|
48
|
+
const knowledgePath = path.join(knowledgeDir, 'knowledge.yaml');
|
|
49
|
+
if (fs.existsSync(knowledgePath)) {
|
|
50
|
+
try {
|
|
51
|
+
const content = fs.readFileSync(knowledgePath, 'utf-8');
|
|
52
|
+
const data = yaml.load(content);
|
|
53
|
+
return data.lessons?.length || 0;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
// Fall through to legacy
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Priority 2: Legacy files
|
|
47
60
|
let count = 0;
|
|
48
61
|
const files = ['lessons-learned.yaml', 'mistakes.yaml', 'improvements.yaml'];
|
|
49
62
|
|
|
@@ -52,7 +65,6 @@ function getLessonsCount() {
|
|
|
52
65
|
if (fs.existsSync(filePath)) {
|
|
53
66
|
try {
|
|
54
67
|
const content = fs.readFileSync(filePath, 'utf-8');
|
|
55
|
-
// Simple YAML parsing - count items in lessons/mistakes/improvements arrays
|
|
56
68
|
const matches = content.match(/^\s*-\s+id:/gm);
|
|
57
69
|
if (matches) count += matches.length;
|
|
58
70
|
} catch (e) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pikakit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.19",
|
|
4
4
|
"description": "Enterprise-grade Agent Skill Manager with Antigravity Skills support, Progressive Disclosure detection, and semantic routing validation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "pikakit <pikakit@gmail.com>",
|