add-skill-kit 3.2.2 → 3.2.4
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/bin/lib/commands/install.js +67 -45
- package/lib/agent-cli/README.md +21 -0
- package/lib/agent-cli/bin/ag-smart.js +158 -0
- package/lib/agent-cli/lib/audit.js +154 -0
- package/lib/agent-cli/lib/audit.test.js +100 -0
- package/lib/agent-cli/lib/auto-learn.js +319 -0
- package/lib/agent-cli/lib/auto_preview.py +148 -0
- package/lib/agent-cli/lib/backup.js +138 -0
- package/lib/agent-cli/lib/backup.test.js +78 -0
- package/lib/agent-cli/lib/checklist.py +222 -0
- package/lib/agent-cli/lib/cognitive-lesson.js +476 -0
- package/lib/agent-cli/lib/completion.js +149 -0
- package/lib/agent-cli/lib/config.js +35 -0
- package/lib/agent-cli/lib/eslint-fix.js +238 -0
- package/lib/agent-cli/lib/evolution-signal.js +215 -0
- package/lib/agent-cli/lib/export.js +86 -0
- package/lib/agent-cli/lib/export.test.js +65 -0
- package/lib/agent-cli/lib/fix.js +337 -0
- package/lib/agent-cli/lib/fix.test.js +80 -0
- package/lib/agent-cli/lib/gemini-export.js +83 -0
- package/lib/agent-cli/lib/generate-registry.js +42 -0
- package/lib/agent-cli/lib/hooks/install-hooks.js +152 -0
- package/lib/agent-cli/lib/hooks/lint-learn.js +172 -0
- package/lib/agent-cli/lib/ignore.js +116 -0
- package/lib/agent-cli/lib/ignore.test.js +58 -0
- package/lib/agent-cli/lib/init.js +124 -0
- package/lib/agent-cli/lib/learn.js +255 -0
- package/lib/agent-cli/lib/learn.test.js +70 -0
- package/lib/agent-cli/lib/migrate-to-v4.js +322 -0
- package/lib/agent-cli/lib/proposals.js +199 -0
- package/lib/agent-cli/lib/proposals.test.js +56 -0
- package/lib/agent-cli/lib/recall.js +820 -0
- package/lib/agent-cli/lib/recall.test.js +107 -0
- package/lib/agent-cli/lib/selfevolution-bridge.js +167 -0
- package/lib/agent-cli/lib/session_manager.py +120 -0
- package/lib/agent-cli/lib/settings.js +203 -0
- package/lib/agent-cli/lib/skill-learn.js +296 -0
- package/lib/agent-cli/lib/stats.js +132 -0
- package/lib/agent-cli/lib/stats.test.js +94 -0
- package/lib/agent-cli/lib/types.js +33 -0
- package/lib/agent-cli/lib/ui/audit-ui.js +146 -0
- package/lib/agent-cli/lib/ui/backup-ui.js +107 -0
- package/lib/agent-cli/lib/ui/clack-helpers.js +317 -0
- package/lib/agent-cli/lib/ui/common.js +83 -0
- package/lib/agent-cli/lib/ui/completion-ui.js +126 -0
- package/lib/agent-cli/lib/ui/custom-select.js +69 -0
- package/lib/agent-cli/lib/ui/dashboard-ui.js +123 -0
- package/lib/agent-cli/lib/ui/evolution-signals-ui.js +107 -0
- package/lib/agent-cli/lib/ui/export-ui.js +94 -0
- package/lib/agent-cli/lib/ui/fix-all-ui.js +191 -0
- package/lib/agent-cli/lib/ui/help-ui.js +49 -0
- package/lib/agent-cli/lib/ui/index.js +169 -0
- package/lib/agent-cli/lib/ui/init-ui.js +56 -0
- package/lib/agent-cli/lib/ui/knowledge-ui.js +55 -0
- package/lib/agent-cli/lib/ui/learn-ui.js +706 -0
- package/lib/agent-cli/lib/ui/lessons-ui.js +148 -0
- package/lib/agent-cli/lib/ui/pretty.js +145 -0
- package/lib/agent-cli/lib/ui/proposals-ui.js +99 -0
- package/lib/agent-cli/lib/ui/recall-ui.js +342 -0
- package/lib/agent-cli/lib/ui/routing-demo.js +79 -0
- package/lib/agent-cli/lib/ui/routing-ui.js +325 -0
- package/lib/agent-cli/lib/ui/settings-ui.js +381 -0
- package/lib/agent-cli/lib/ui/stats-ui.js +123 -0
- package/lib/agent-cli/lib/ui/watch-ui.js +236 -0
- package/lib/agent-cli/lib/verify_all.py +327 -0
- package/lib/agent-cli/lib/watcher.js +181 -0
- package/lib/agent-cli/lib/watcher.test.js +85 -0
- package/lib/agent-cli/package.json +51 -0
- package/lib/agentskillskit-cli/README.md +21 -0
- package/lib/agentskillskit-cli/ag-smart.js +158 -0
- package/lib/agentskillskit-cli/package.json +51 -0
- package/package.json +11 -6
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Smart Learning Script - ESM Version (Production-Ready)
|
|
4
|
+
*
|
|
5
|
+
* The "Teacher" script. Adds new lessons to the system memory.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Manual lesson addition
|
|
9
|
+
* - Category tagging
|
|
10
|
+
* - Source tracking (manual, eslint, test-failure)
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* ag-smart learn --add --pattern "regex" --message "why bad"
|
|
14
|
+
* ag-smart learn --list
|
|
15
|
+
* ag-smart learn --remove <id>
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
import path from "path";
|
|
20
|
+
import yaml from "js-yaml";
|
|
21
|
+
import { KNOWLEDGE_DIR, LESSONS_PATH, DEBUG, VERSION } from "./config.js";
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// CORE FUNCTIONS
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Load knowledge base from YAML file
|
|
29
|
+
* @returns {{ lessons: Array, version?: number }}
|
|
30
|
+
*/
|
|
31
|
+
function loadKnowledge() {
|
|
32
|
+
try {
|
|
33
|
+
if (!fs.existsSync(LESSONS_PATH)) {
|
|
34
|
+
const initial = { lessons: [], version: 1 };
|
|
35
|
+
fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true });
|
|
36
|
+
fs.writeFileSync(LESSONS_PATH, yaml.dump(initial), "utf8");
|
|
37
|
+
return initial;
|
|
38
|
+
}
|
|
39
|
+
const content = fs.readFileSync(LESSONS_PATH, "utf8");
|
|
40
|
+
return yaml.load(content) || { lessons: [], version: 1 };
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error("❌ Failed to load knowledge base:", error.message);
|
|
43
|
+
if (DEBUG) console.error(error.stack);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Save knowledge base to YAML file
|
|
50
|
+
* @param {{ lessons: Array, version?: number }} data
|
|
51
|
+
*/
|
|
52
|
+
function saveKnowledge(data) {
|
|
53
|
+
try {
|
|
54
|
+
fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true });
|
|
55
|
+
const str = yaml.dump(data, { lineWidth: -1, quotingType: '"' });
|
|
56
|
+
fs.writeFileSync(LESSONS_PATH, str, "utf8");
|
|
57
|
+
console.log("✅ Knowledge base updated successfully.");
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("❌ Failed to save knowledge base:", error.message);
|
|
60
|
+
if (DEBUG) console.error(error.stack);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Add a new lesson to the knowledge base
|
|
67
|
+
* @param {string} pattern - Regex pattern
|
|
68
|
+
* @param {string} message - Explanation message
|
|
69
|
+
* @param {string} severity - WARNING or ERROR
|
|
70
|
+
* @param {string} category - Category tag
|
|
71
|
+
*/
|
|
72
|
+
function addLesson(pattern, message, severity = "WARNING", category = "general") {
|
|
73
|
+
const db = loadKnowledge();
|
|
74
|
+
|
|
75
|
+
// Validate Regex
|
|
76
|
+
try {
|
|
77
|
+
new RegExp(pattern);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.error("❌ Invalid Regex pattern:", e.message);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Validate severity
|
|
84
|
+
if (!["WARNING", "ERROR"].includes(severity.toUpperCase())) {
|
|
85
|
+
console.error("❌ Invalid severity. Must be WARNING or ERROR");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check for duplicates
|
|
90
|
+
const exists = db.lessons.some(l => l.pattern === pattern);
|
|
91
|
+
if (exists) {
|
|
92
|
+
console.log("⚠️ Pattern already exists in knowledge base.");
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const id = `LEARN-${String(db.lessons.length + 1).padStart(3, "0")}`;
|
|
97
|
+
|
|
98
|
+
const lesson = {
|
|
99
|
+
id,
|
|
100
|
+
pattern,
|
|
101
|
+
message,
|
|
102
|
+
severity: severity.toUpperCase(),
|
|
103
|
+
category,
|
|
104
|
+
source: "manual",
|
|
105
|
+
hitCount: 0,
|
|
106
|
+
lastHit: null,
|
|
107
|
+
autoEscalated: false,
|
|
108
|
+
addedAt: new Date().toISOString()
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
db.lessons.push(lesson);
|
|
112
|
+
saveKnowledge(db);
|
|
113
|
+
|
|
114
|
+
console.log(`\n🎓 Lesson Learned: [${id}]`);
|
|
115
|
+
console.log(` Pattern: /${pattern}/`);
|
|
116
|
+
console.log(` Message: ${message}`);
|
|
117
|
+
console.log(` Severity: ${severity.toUpperCase()}`);
|
|
118
|
+
console.log(` Category: ${category}\n`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Remove a lesson by ID
|
|
123
|
+
* @param {string} lessonId
|
|
124
|
+
*/
|
|
125
|
+
function removeLesson(lessonId) {
|
|
126
|
+
const db = loadKnowledge();
|
|
127
|
+
const idx = db.lessons.findIndex(l => l.id === lessonId.toUpperCase());
|
|
128
|
+
|
|
129
|
+
if (idx === -1) {
|
|
130
|
+
console.log(`❌ Lesson not found: ${lessonId}`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const removed = db.lessons.splice(idx, 1)[0];
|
|
135
|
+
saveKnowledge(db);
|
|
136
|
+
|
|
137
|
+
console.log(`\n🗑️ Removed lesson: [${removed.id}]`);
|
|
138
|
+
console.log(` Pattern: /${removed.pattern}/\n`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* List all learned lessons
|
|
143
|
+
* @param {string} category - Filter by category
|
|
144
|
+
*/
|
|
145
|
+
function listLessons(category = null) {
|
|
146
|
+
const db = loadKnowledge();
|
|
147
|
+
|
|
148
|
+
if (!db.lessons || db.lessons.length === 0) {
|
|
149
|
+
console.log("\nℹ️ No lessons learned yet.");
|
|
150
|
+
console.log(" Use: ag-smart learn --add --pattern \"pat\" --message \"msg\"\n");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let lessons = db.lessons;
|
|
155
|
+
if (category) {
|
|
156
|
+
lessons = lessons.filter(l => l.category === category);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log(`\n🧠 Smart Agent Knowledge Base (${lessons.length} lesson(s))\n`);
|
|
160
|
+
console.log("─".repeat(60));
|
|
161
|
+
|
|
162
|
+
lessons.forEach(l => {
|
|
163
|
+
const icon = l.severity === "ERROR" ? "❌" : "⚠️";
|
|
164
|
+
const hits = l.hitCount ? ` (${l.hitCount} hits)` : "";
|
|
165
|
+
const escalated = l.autoEscalated ? " ⚡" : "";
|
|
166
|
+
|
|
167
|
+
console.log(`${icon} [${l.id}] ${l.message}${hits}${escalated}`);
|
|
168
|
+
console.log(` Pattern: /${l.pattern}/`);
|
|
169
|
+
console.log(` Category: ${l.category || "general"} | Source: ${l.source || "manual"}`);
|
|
170
|
+
console.log("");
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// CLI
|
|
176
|
+
// ============================================================================
|
|
177
|
+
|
|
178
|
+
function printHelp() {
|
|
179
|
+
console.log(`
|
|
180
|
+
🎓 Smart Learning Tool v${VERSION}
|
|
181
|
+
|
|
182
|
+
USAGE:
|
|
183
|
+
ag-smart learn --add --pattern "..." --message "..."
|
|
184
|
+
ag-smart learn --list [--category <cat>]
|
|
185
|
+
ag-smart learn --remove <ID>
|
|
186
|
+
|
|
187
|
+
OPTIONS:
|
|
188
|
+
--add Add a new lesson
|
|
189
|
+
--pattern Regex pattern to flag
|
|
190
|
+
--message Explanation message
|
|
191
|
+
--severity WARNING (default) or ERROR
|
|
192
|
+
--category Category tag (default: general)
|
|
193
|
+
--list List all lessons
|
|
194
|
+
--remove Remove lesson by ID
|
|
195
|
+
--help Show this help
|
|
196
|
+
|
|
197
|
+
EXAMPLES:
|
|
198
|
+
ag-smart learn --add --pattern "console\\.log" --message "No console.log in production" --severity ERROR
|
|
199
|
+
ag-smart learn --list
|
|
200
|
+
ag-smart learn --remove LEARN-001
|
|
201
|
+
`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function main() {
|
|
205
|
+
const args = process.argv.slice(2);
|
|
206
|
+
|
|
207
|
+
if (args.includes("--help") || args.length === 0) {
|
|
208
|
+
printHelp();
|
|
209
|
+
process.exit(0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (args.includes("--list")) {
|
|
213
|
+
const catIdx = args.indexOf("--category");
|
|
214
|
+
const category = catIdx !== -1 ? args[catIdx + 1] : null;
|
|
215
|
+
listLessons(category);
|
|
216
|
+
process.exit(0);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (args.includes("--remove")) {
|
|
220
|
+
const removeIdx = args.indexOf("--remove");
|
|
221
|
+
const lessonId = args[removeIdx + 1];
|
|
222
|
+
if (!lessonId) {
|
|
223
|
+
console.error("❌ Missing lesson ID for --remove");
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
removeLesson(lessonId);
|
|
227
|
+
process.exit(0);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (args.includes("--add")) {
|
|
231
|
+
const pIdx = args.indexOf("--pattern");
|
|
232
|
+
const mIdx = args.indexOf("--message");
|
|
233
|
+
const sIdx = args.indexOf("--severity");
|
|
234
|
+
const cIdx = args.indexOf("--category");
|
|
235
|
+
|
|
236
|
+
if (pIdx === -1 || mIdx === -1) {
|
|
237
|
+
console.error("❌ --pattern and --message are required for --add");
|
|
238
|
+
process.exit(1);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const pattern = args[pIdx + 1];
|
|
242
|
+
const message = args[mIdx + 1];
|
|
243
|
+
const severity = sIdx !== -1 ? args[sIdx + 1] : "WARNING";
|
|
244
|
+
const category = cIdx !== -1 ? args[cIdx + 1] : "general";
|
|
245
|
+
|
|
246
|
+
if (!pattern || !message) {
|
|
247
|
+
console.error("❌ Missing values for pattern/message");
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
addLesson(pattern, message, severity, category);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
main();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Tests for learn.js functionality
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import os from "os";
|
|
8
|
+
|
|
9
|
+
// Mock config để test
|
|
10
|
+
const TEST_DIR = path.join(os.tmpdir(), "agent-skill-kit-test");
|
|
11
|
+
const KNOWLEDGE_DIR = path.join(TEST_DIR, ".agent", "knowledge");
|
|
12
|
+
const LESSONS_PATH = path.join(KNOWLEDGE_DIR, "lessons-learned.yaml");
|
|
13
|
+
|
|
14
|
+
describe("Knowledge Base Operations", () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
fs.mkdirSync(KNOWLEDGE_DIR, { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("creates initial knowledge file if missing", () => {
|
|
24
|
+
// Simulate missing file scenario
|
|
25
|
+
expect(fs.existsSync(LESSONS_PATH)).toBe(false);
|
|
26
|
+
|
|
27
|
+
// Create initial file
|
|
28
|
+
const initial = { lessons: [] };
|
|
29
|
+
fs.writeFileSync(LESSONS_PATH, JSON.stringify(initial), "utf8");
|
|
30
|
+
|
|
31
|
+
expect(fs.existsSync(LESSONS_PATH)).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("can write and read lessons", () => {
|
|
35
|
+
const lesson = {
|
|
36
|
+
id: "LEARN-001",
|
|
37
|
+
pattern: "console\\.log",
|
|
38
|
+
message: "No console.log in production",
|
|
39
|
+
severity: "WARNING",
|
|
40
|
+
addedAt: new Date().toISOString()
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const data = { lessons: [lesson] };
|
|
44
|
+
fs.writeFileSync(LESSONS_PATH, JSON.stringify(data), "utf8");
|
|
45
|
+
|
|
46
|
+
const content = fs.readFileSync(LESSONS_PATH, "utf8");
|
|
47
|
+
const parsed = JSON.parse(content);
|
|
48
|
+
|
|
49
|
+
expect(parsed.lessons).toHaveLength(1);
|
|
50
|
+
expect(parsed.lessons[0].id).toBe("LEARN-001");
|
|
51
|
+
expect(parsed.lessons[0].pattern).toBe("console\\.log");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("validates regex patterns", () => {
|
|
55
|
+
const validPattern = "console\\.log";
|
|
56
|
+
const invalidPattern = "[invalid(";
|
|
57
|
+
|
|
58
|
+
expect(() => new RegExp(validPattern)).not.toThrow();
|
|
59
|
+
expect(() => new RegExp(invalidPattern)).toThrow();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("validates severity values", () => {
|
|
63
|
+
const validSeverities = ["WARNING", "ERROR"];
|
|
64
|
+
const invalidSeverity = "INFO";
|
|
65
|
+
|
|
66
|
+
expect(validSeverities.includes("WARNING")).toBe(true);
|
|
67
|
+
expect(validSeverities.includes("ERROR")).toBe(true);
|
|
68
|
+
expect(validSeverities.includes(invalidSeverity)).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Migration Script: v3.x → v4.x Cognitive Lesson Engine
|
|
4
|
+
*
|
|
5
|
+
* This script:
|
|
6
|
+
* 1. Backs up current lessons-learned.yaml
|
|
7
|
+
* 2. Classifies lessons into mistakes vs improvements
|
|
8
|
+
* 3. Generates mistakes.yaml and improvements.yaml
|
|
9
|
+
* 4. Preserves all existing data
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import yaml from 'js-yaml';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
|
|
17
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const PROJECT_ROOT = path.join(__dirname, '../../..');
|
|
19
|
+
const KNOWLEDGE_DIR = path.join(PROJECT_ROOT, '.agent/knowledge');
|
|
20
|
+
const LESSONS_PATH = path.join(KNOWLEDGE_DIR, 'lessons-learned.yaml');
|
|
21
|
+
const BACKUP_DIR = path.join(KNOWLEDGE_DIR, '_migration_backup');
|
|
22
|
+
const MISTAKES_PATH = path.join(KNOWLEDGE_DIR, 'mistakes.yaml');
|
|
23
|
+
const IMPROVEMENTS_PATH = path.join(KNOWLEDGE_DIR, 'improvements.yaml');
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// STEP 1: Backup Current Data
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
function backupCurrentLessons() {
|
|
30
|
+
console.log('📦 Step 1: Backing up current lessons...');
|
|
31
|
+
|
|
32
|
+
if (!fs.existsSync(LESSONS_PATH)) {
|
|
33
|
+
console.log('⚠️ No lessons-learned.yaml found, skipping backup');
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Create backup directory
|
|
38
|
+
fs.mkdirSync(BACKUP_DIR, { recursive: true });
|
|
39
|
+
|
|
40
|
+
// Backup with timestamp
|
|
41
|
+
const timestamp = new Date().toISOString().replace(/:/g, '-');
|
|
42
|
+
const backupPath = path.join(BACKUP_DIR, `lessons-learned_${timestamp}.yaml`);
|
|
43
|
+
|
|
44
|
+
fs.copyFileSync(LESSONS_PATH, backupPath);
|
|
45
|
+
console.log(`✅ Backup created: ${backupPath}`);
|
|
46
|
+
|
|
47
|
+
// Load and return data
|
|
48
|
+
const content = fs.readFileSync(LESSONS_PATH, 'utf8');
|
|
49
|
+
return yaml.load(content);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ============================================================================
|
|
53
|
+
// STEP 2: Classification Logic
|
|
54
|
+
// ============================================================================
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Auto-classify lesson as mistake or improvement
|
|
58
|
+
* Conservative: default to mistake (safer assumption)
|
|
59
|
+
*/
|
|
60
|
+
function classifyLesson(lesson) {
|
|
61
|
+
const message = lesson.message.toLowerCase();
|
|
62
|
+
|
|
63
|
+
// Keywords that indicate improvement/best practice
|
|
64
|
+
const improvementKeywords = [
|
|
65
|
+
'use ', 'always use', 'prefer ', 'should use',
|
|
66
|
+
'best practice', 'recommended', 'instead use',
|
|
67
|
+
'better to', 'proper way', 'correct approach'
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// Keywords that indicate mistake/anti-pattern
|
|
71
|
+
const mistakeKeywords = [
|
|
72
|
+
'never', 'avoid', 'don\'t', 'do not', 'incorrect',
|
|
73
|
+
'wrong', 'bad', 'conflicts', 'causes', 'breaks'
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const hasMistakeKeyword = mistakeKeywords.some(kw => message.includes(kw));
|
|
77
|
+
const hasImprovementKeyword = improvementKeywords.some(kw => message.includes(kw));
|
|
78
|
+
|
|
79
|
+
// Both keywords present - need manual review
|
|
80
|
+
if (hasMistakeKeyword && hasImprovementKeyword) {
|
|
81
|
+
return 'mixed';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Clear improvement
|
|
85
|
+
if (hasImprovementKeyword && !hasMistakeKeyword) {
|
|
86
|
+
return 'improvement';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Default: mistake (conservative)
|
|
90
|
+
return 'mistake';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Extract tags from lesson data
|
|
95
|
+
* Tags used for grouping into Cognitive Lessons
|
|
96
|
+
*/
|
|
97
|
+
function extractTags(lesson) {
|
|
98
|
+
const tags = new Set();
|
|
99
|
+
|
|
100
|
+
// Add category as tag
|
|
101
|
+
if (lesson.category) {
|
|
102
|
+
tags.add(lesson.category);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Extract from pattern
|
|
106
|
+
const pattern = lesson.pattern.toLowerCase();
|
|
107
|
+
if (pattern.includes('select')) tags.add('cli-navigation');
|
|
108
|
+
if (pattern.includes('menu')) tags.add('ux');
|
|
109
|
+
if (pattern.includes('rename') || pattern.includes('move') || pattern.includes('rebrand')) {
|
|
110
|
+
tags.add('file-safety');
|
|
111
|
+
tags.add('rebranding');
|
|
112
|
+
}
|
|
113
|
+
if (pattern.includes('import')) tags.add('imports');
|
|
114
|
+
if (pattern.includes('recursive')) tags.add('architecture');
|
|
115
|
+
|
|
116
|
+
// Extract from message
|
|
117
|
+
const message = lesson.message.toLowerCase();
|
|
118
|
+
if (message.includes('esc')) tags.add('cli-navigation');
|
|
119
|
+
if (message.includes('clack')) tags.add('clack-framework');
|
|
120
|
+
if (message.includes('menu')) tags.add('ux');
|
|
121
|
+
if (message.includes('security')) tags.add('security');
|
|
122
|
+
if (message.includes('performance')) tags.add('performance');
|
|
123
|
+
|
|
124
|
+
return Array.from(tags);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Split mixed lessons into mistake + improvement
|
|
129
|
+
*/
|
|
130
|
+
function splitMixedLesson(lesson) {
|
|
131
|
+
const message = lesson.message;
|
|
132
|
+
|
|
133
|
+
// Try to extract both parts
|
|
134
|
+
// Pattern: "NEVER X. Use Y instead"
|
|
135
|
+
const neverMatch = message.match(/(NEVER|Don't|Avoid)\s+([^.]+)\./i);
|
|
136
|
+
const useMatch = message.match(/(Use|Always use|Instead use)\s+([^.]+)/i);
|
|
137
|
+
|
|
138
|
+
const mistake = {
|
|
139
|
+
...lesson,
|
|
140
|
+
id: lesson.id.replace('LEARN', 'MISTAKE'),
|
|
141
|
+
title: neverMatch ? neverMatch[0] : message.split('.')[0],
|
|
142
|
+
message: neverMatch ? neverMatch[0] : message.split('.')[0],
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
let improvement = null;
|
|
146
|
+
if (useMatch) {
|
|
147
|
+
improvement = {
|
|
148
|
+
id: lesson.id.replace('LEARN', 'IMPROVE'),
|
|
149
|
+
title: useMatch[0],
|
|
150
|
+
message: useMatch[0],
|
|
151
|
+
pattern: lesson.pattern,
|
|
152
|
+
priority: lesson.severity === 'ERROR' ? 'HIGH' : 'MEDIUM',
|
|
153
|
+
tags: extractTags(lesson),
|
|
154
|
+
added: lesson.addedAt,
|
|
155
|
+
appliedCount: 0,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { mistake, improvement };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ============================================================================
|
|
163
|
+
// STEP 3: Migration Logic
|
|
164
|
+
// ============================================================================
|
|
165
|
+
|
|
166
|
+
function migrateToV4(oldData) {
|
|
167
|
+
console.log('\n🔄 Step 2: Classifying lessons...');
|
|
168
|
+
|
|
169
|
+
const mistakes = [];
|
|
170
|
+
const improvements = [];
|
|
171
|
+
const needsReview = [];
|
|
172
|
+
|
|
173
|
+
oldData.lessons.forEach((lesson, index) => {
|
|
174
|
+
const classification = classifyLesson(lesson);
|
|
175
|
+
const tags = extractTags(lesson);
|
|
176
|
+
|
|
177
|
+
console.log(` ${lesson.id}: ${classification} (tags: ${tags.join(', ')})`);
|
|
178
|
+
|
|
179
|
+
if (classification === 'mistake') {
|
|
180
|
+
mistakes.push({
|
|
181
|
+
id: lesson.id.replace('LEARN', 'MISTAKE'),
|
|
182
|
+
title: lesson.message.split('.')[0], // First sentence as title
|
|
183
|
+
pattern: lesson.pattern,
|
|
184
|
+
message: lesson.message,
|
|
185
|
+
severity: lesson.severity,
|
|
186
|
+
tags,
|
|
187
|
+
context: lesson.category,
|
|
188
|
+
hitCount: lesson.hitCount || 0,
|
|
189
|
+
lastHit: lesson.lastHit,
|
|
190
|
+
added: lesson.addedAt,
|
|
191
|
+
source: lesson.source,
|
|
192
|
+
excludePaths: lesson.excludePaths || [],
|
|
193
|
+
});
|
|
194
|
+
} else if (classification === 'improvement') {
|
|
195
|
+
improvements.push({
|
|
196
|
+
id: lesson.id.replace('LEARN', 'IMPROVE'),
|
|
197
|
+
title: lesson.message.split('.')[0],
|
|
198
|
+
pattern: lesson.pattern,
|
|
199
|
+
message: lesson.message,
|
|
200
|
+
priority: lesson.severity === 'ERROR' ? 'HIGH' : 'MEDIUM',
|
|
201
|
+
tags,
|
|
202
|
+
added: lesson.addedAt,
|
|
203
|
+
appliedCount: lesson.hitCount || 0,
|
|
204
|
+
lastApplied: lesson.lastHit,
|
|
205
|
+
source: lesson.source,
|
|
206
|
+
});
|
|
207
|
+
} else {
|
|
208
|
+
// Mixed - split into both
|
|
209
|
+
const { mistake, improvement } = splitMixedLesson(lesson);
|
|
210
|
+
mistakes.push({
|
|
211
|
+
...mistake,
|
|
212
|
+
severity: lesson.severity,
|
|
213
|
+
tags,
|
|
214
|
+
hitCount: lesson.hitCount || 0,
|
|
215
|
+
lastHit: lesson.lastHit,
|
|
216
|
+
added: lesson.addedAt,
|
|
217
|
+
source: lesson.source,
|
|
218
|
+
excludePaths: lesson.excludePaths || [],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
if (improvement) {
|
|
222
|
+
improvements.push({
|
|
223
|
+
...improvement,
|
|
224
|
+
source: lesson.source,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
needsReview.push({
|
|
229
|
+
...lesson,
|
|
230
|
+
reason: 'Mixed mistake + improvement keywords, auto-split but needs verification',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
console.log(`\n✅ Classification complete:`);
|
|
236
|
+
console.log(` ${mistakes.length} mistakes`);
|
|
237
|
+
console.log(` ${improvements.length} improvements`);
|
|
238
|
+
console.log(` ${needsReview.length} need manual review`);
|
|
239
|
+
|
|
240
|
+
return { mistakes, improvements, needsReview };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ============================================================================
|
|
244
|
+
// STEP 4: Save New Files
|
|
245
|
+
// ============================================================================
|
|
246
|
+
|
|
247
|
+
function saveYAML(filename, data) {
|
|
248
|
+
const filepath = path.join(KNOWLEDGE_DIR, filename);
|
|
249
|
+
const yamlStr = yaml.dump(data, {
|
|
250
|
+
lineWidth: -1,
|
|
251
|
+
noRefs: true,
|
|
252
|
+
sortKeys: false,
|
|
253
|
+
});
|
|
254
|
+
fs.writeFileSync(filepath, yamlStr, 'utf8');
|
|
255
|
+
console.log(`✅ Created: ${filename}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function saveNewStructure(result) {
|
|
259
|
+
console.log('\n📝 Step 3: Creating new data files...');
|
|
260
|
+
|
|
261
|
+
// Save mistakes
|
|
262
|
+
saveYAML('mistakes.yaml', {
|
|
263
|
+
version: 4.0,
|
|
264
|
+
mistakes: result.mistakes,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Save improvements
|
|
268
|
+
saveYAML('improvements.yaml', {
|
|
269
|
+
version: 4.0,
|
|
270
|
+
improvements: result.improvements,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Save review queue if needed
|
|
274
|
+
if (result.needsReview.length > 0) {
|
|
275
|
+
saveYAML('_needs_review.yaml', {
|
|
276
|
+
note: 'These lessons contained both mistake and improvement keywords and were auto-split. Please review.',
|
|
277
|
+
items: result.needsReview,
|
|
278
|
+
});
|
|
279
|
+
console.log(`⚠️ ${result.needsReview.length} items saved to _needs_review.yaml`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// MAIN MIGRATION
|
|
285
|
+
// ============================================================================
|
|
286
|
+
|
|
287
|
+
async function main() {
|
|
288
|
+
console.log('🧠 Cognitive Lesson Engine v4.x Migration\n');
|
|
289
|
+
console.log('This script will transform your lessons into the new architecture.');
|
|
290
|
+
console.log('All existing data will be preserved in backups.\n');
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
// Step 1: Backup
|
|
294
|
+
const oldData = backupCurrentLessons();
|
|
295
|
+
|
|
296
|
+
if (!oldData || !oldData.lessons || oldData.lessons.length === 0) {
|
|
297
|
+
console.log('❌ No lessons found to migrate.');
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Step 2: Classify
|
|
302
|
+
const result = migrateToV4(oldData);
|
|
303
|
+
|
|
304
|
+
// Step 3: Save
|
|
305
|
+
saveNewStructure(result);
|
|
306
|
+
|
|
307
|
+
// Step 4: Summary
|
|
308
|
+
console.log('\n🎉 Migration complete!');
|
|
309
|
+
console.log('\nNext steps:');
|
|
310
|
+
console.log(' 1. Review _needs_review.yaml (if exists)');
|
|
311
|
+
console.log(' 2. Test with: node packages/cli/lib/ui/index.js');
|
|
312
|
+
console.log(' 3. Original file preserved in _migration_backup/');
|
|
313
|
+
console.log('\n✅ You can now use the Cognitive Lesson Engine!');
|
|
314
|
+
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.error('\n❌ Migration failed:', error.message);
|
|
317
|
+
console.error(error.stack);
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
main();
|