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 YAML file
146
- * Supports both v3.x (lessons-learned.yaml) and v4.x (cognitive lessons)
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
- // Check for v4.x cognitive structure first
152
- const mistakesPath = path.join(KNOWLEDGE_DIR, 'mistakes.yaml');
153
- const improvementsPath = path.join(KNOWLEDGE_DIR, 'improvements.yaml');
154
-
155
- const hasV4 = fs.existsSync(mistakesPath) || fs.existsSync(improvementsPath);
156
-
157
- if (hasV4) {
158
- // v4.x: Load cognitive lessons and flatten for scanning
159
- const mistakes = fs.existsSync(mistakesPath)
160
- ? yaml.load(fs.readFileSync(mistakesPath, 'utf8'))
161
- : { mistakes: [] };
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
- // Fallback to v3.x
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 YAML file
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
- fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true });
219
-
220
- // v4.x: Save to mistakes.yaml and improvements.yaml
221
- if (data.version === 4.0) {
222
- const mistakes = data.lessons.filter(l => l.type === 'mistake').map(m => {
223
- const { type, ...rest } = m; // Remove type field
224
- return rest;
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
- // Save improvements
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 from YAML files (same source as CLI)
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.18",
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>",