chainlesschain 0.45.74 → 0.45.76
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -15
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-BhJ3YFWt.js → AppLayout-2RCrdXxl.js} +1 -1
- package/src/assets/web-panel/assets/AppLayout-D9pBLPC3.css +1 -0
- package/src/assets/web-panel/assets/{Chat-DaxTP3x8.js → Chat-B2nB8o_F.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-CjlX4CrX.js → Dashboard-DanoHPSI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BCvgBkD3.js → Skills-CLlblJcG.js} +1 -1
- package/src/assets/web-panel/assets/chat-DWBA4-cl.js +1 -0
- package/src/assets/web-panel/assets/{index-DrmEk9S3.js → index-CyGtHm63.js} +2 -2
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/learning.js +273 -0
- package/src/commands/lowcode.js +23 -8
- package/src/gateways/discord/discord-formatter.js +89 -0
- package/src/gateways/gateway-base.js +189 -0
- package/src/gateways/telegram/telegram-formatter.js +93 -0
- package/src/index.js +2 -0
- package/src/lib/app-builder.js +136 -8
- package/src/lib/autonomous-agent.js +8 -1
- package/src/lib/cli-context-engineering.js +15 -0
- package/src/lib/execution-backend.js +239 -0
- package/src/lib/hook-manager.js +2 -0
- package/src/lib/iteration-budget.js +175 -0
- package/src/lib/learning/learning-hooks.js +117 -0
- package/src/lib/learning/learning-tables.js +66 -0
- package/src/lib/learning/outcome-feedback.js +243 -0
- package/src/lib/learning/reflection-engine.js +323 -0
- package/src/lib/learning/skill-improver.js +536 -0
- package/src/lib/learning/skill-synthesizer.js +315 -0
- package/src/lib/learning/trajectory-store.js +409 -0
- package/src/lib/plugin-autodiscovery.js +224 -0
- package/src/lib/session-search.js +193 -0
- package/src/lib/sub-agent-context.js +7 -2
- package/src/lib/user-profile.js +172 -0
- package/src/lib/web-ui-server.js +1 -1
- package/src/repl/agent-repl.js +109 -0
- package/src/runtime/agent-core.js +75 -4
- package/src/runtime/coding-agent-contract-shared.cjs +35 -0
- package/src/runtime/coding-agent-policy.cjs +10 -0
- package/src/assets/web-panel/assets/AppLayout-Cr2lWhF-.css +0 -1
- package/src/assets/web-panel/assets/chat-BmwHBi9M.js +0 -1
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SkillImprover — Iteratively improves auto-synthesized SKILL.md files
|
|
3
|
+
* based on execution feedback and better trajectories.
|
|
4
|
+
*
|
|
5
|
+
* Three improvement triggers:
|
|
6
|
+
* 1. repairFromError — skill execution failed, patch the procedure
|
|
7
|
+
* 2. updateFromCorrection — user corrected the agent, learn the delta
|
|
8
|
+
* 3. improveFromBetterTrajectory — a higher-scoring trajectory for
|
|
9
|
+
* the same tool pattern was found, merge improvements
|
|
10
|
+
*
|
|
11
|
+
* All changes are logged to skill_improvement_log for auditability.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from "fs";
|
|
15
|
+
import path from "path";
|
|
16
|
+
|
|
17
|
+
// ── _deps for test injection ────────────────────────────
|
|
18
|
+
const _deps = { fs, path };
|
|
19
|
+
|
|
20
|
+
// ── Helpers ─────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Bump a semver-like version string (e.g. "1.0.0" → "1.1.0").
|
|
24
|
+
* Bumps the minor version.
|
|
25
|
+
* @param {string} version
|
|
26
|
+
* @returns {string}
|
|
27
|
+
*/
|
|
28
|
+
export function bumpVersion(version) {
|
|
29
|
+
if (!version) return "1.1.0";
|
|
30
|
+
const parts = version.split(".");
|
|
31
|
+
if (parts.length < 3) return "1.1.0";
|
|
32
|
+
const major = parseInt(parts[0], 10) || 1;
|
|
33
|
+
const minor = parseInt(parts[1], 10) || 0;
|
|
34
|
+
return `${major}.${minor + 1}.0`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Parse simple YAML frontmatter from SKILL.md content.
|
|
39
|
+
* Returns { meta: {}, body: string }.
|
|
40
|
+
* @param {string} content
|
|
41
|
+
* @returns {{ meta: Record<string, string>, body: string }}
|
|
42
|
+
*/
|
|
43
|
+
export function parseSkillFrontmatter(content) {
|
|
44
|
+
const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
45
|
+
if (!match) return { meta: {}, body: content };
|
|
46
|
+
|
|
47
|
+
const meta = {};
|
|
48
|
+
for (const line of match[1].split("\n")) {
|
|
49
|
+
const idx = line.indexOf(":");
|
|
50
|
+
if (idx > 0) {
|
|
51
|
+
const key = line.slice(0, idx).trim();
|
|
52
|
+
const val = line.slice(idx + 1).trim();
|
|
53
|
+
meta[key] = val;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { meta, body: match[2] };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Rebuild SKILL.md from meta + body.
|
|
61
|
+
* @param {Record<string, string>} meta
|
|
62
|
+
* @param {string} body
|
|
63
|
+
* @returns {string}
|
|
64
|
+
*/
|
|
65
|
+
export function rebuildSkillMd(meta, body) {
|
|
66
|
+
const lines = Object.entries(meta).map(([k, v]) => `${k}: ${v}`);
|
|
67
|
+
return `---\n${lines.join("\n")}\n---\n${body}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── LLM prompt builders ────────────────────────────────
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Build LLM prompt for error-based repair.
|
|
74
|
+
* @param {string} skillContent — current SKILL.md
|
|
75
|
+
* @param {object} errorContext — { error, toolChain, userIntent }
|
|
76
|
+
* @returns {Array<{role:string, content:string}>}
|
|
77
|
+
*/
|
|
78
|
+
export function buildRepairPrompt(skillContent, errorContext) {
|
|
79
|
+
return [
|
|
80
|
+
{
|
|
81
|
+
role: "system",
|
|
82
|
+
content: `You are a skill improvement expert. A skill failed during execution.
|
|
83
|
+
Analyze the error and suggest fixes to the skill's procedure.
|
|
84
|
+
Output ONLY valid JSON:
|
|
85
|
+
{
|
|
86
|
+
"diagnosis": "What went wrong",
|
|
87
|
+
"fixedProcedure": ["Step 1", "Step 2", ...],
|
|
88
|
+
"newPitfalls": ["Pitfall description", ...],
|
|
89
|
+
"confidence": 0.0-1.0
|
|
90
|
+
}
|
|
91
|
+
If the skill cannot be improved from this error, respond with: {"not_applicable": true}`,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
role: "user",
|
|
95
|
+
content: `## Current Skill
|
|
96
|
+
${skillContent.slice(0, 1500)}
|
|
97
|
+
|
|
98
|
+
## Error Context
|
|
99
|
+
Error: ${errorContext.error || "unknown"}
|
|
100
|
+
User intent: ${errorContext.userIntent || "unknown"}
|
|
101
|
+
Tool chain: ${JSON.stringify(errorContext.toolChain || []).slice(0, 500)}`,
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Build LLM prompt for correction-based update.
|
|
108
|
+
* @param {string} skillContent
|
|
109
|
+
* @param {object} correctionContext — { userMessage, previousToolChain, correctedToolChain }
|
|
110
|
+
* @returns {Array<{role:string, content:string}>}
|
|
111
|
+
*/
|
|
112
|
+
export function buildCorrectionPrompt(skillContent, correctionContext) {
|
|
113
|
+
return [
|
|
114
|
+
{
|
|
115
|
+
role: "system",
|
|
116
|
+
content: `You are a skill improvement expert. The user corrected the agent's behavior.
|
|
117
|
+
Compare the original and corrected execution to improve the skill.
|
|
118
|
+
Output ONLY valid JSON:
|
|
119
|
+
{
|
|
120
|
+
"whatChanged": "Description of the correction",
|
|
121
|
+
"updatedProcedure": ["Step 1", "Step 2", ...],
|
|
122
|
+
"newPitfalls": ["Pitfall description", ...],
|
|
123
|
+
"confidence": 0.0-1.0
|
|
124
|
+
}
|
|
125
|
+
If the correction is too specific to generalize, respond with: {"not_applicable": true}`,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
role: "user",
|
|
129
|
+
content: `## Current Skill
|
|
130
|
+
${skillContent.slice(0, 1500)}
|
|
131
|
+
|
|
132
|
+
## Correction
|
|
133
|
+
User said: ${correctionContext.userMessage || ""}
|
|
134
|
+
Original tools: ${JSON.stringify(correctionContext.previousToolChain || []).slice(0, 300)}
|
|
135
|
+
Corrected tools: ${JSON.stringify(correctionContext.correctedToolChain || []).slice(0, 300)}`,
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Build LLM prompt for improvement from a better trajectory.
|
|
142
|
+
* @param {string} skillContent
|
|
143
|
+
* @param {object} betterTrajectory
|
|
144
|
+
* @returns {Array<{role:string, content:string}>}
|
|
145
|
+
*/
|
|
146
|
+
export function buildImprovementPrompt(skillContent, betterTrajectory) {
|
|
147
|
+
const toolSteps = (betterTrajectory.toolChain || [])
|
|
148
|
+
.map(
|
|
149
|
+
(t, i) =>
|
|
150
|
+
` ${i + 1}. ${t.tool}(${JSON.stringify(t.args || {}).slice(0, 150)}) → ${t.status}`,
|
|
151
|
+
)
|
|
152
|
+
.join("\n");
|
|
153
|
+
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
role: "system",
|
|
157
|
+
content: `You are a skill improvement expert. A better execution trajectory was found for a similar task.
|
|
158
|
+
Merge improvements into the existing skill.
|
|
159
|
+
Output ONLY valid JSON:
|
|
160
|
+
{
|
|
161
|
+
"improvements": "Summary of what's better",
|
|
162
|
+
"mergedProcedure": ["Step 1", "Step 2", ...],
|
|
163
|
+
"mergedPitfalls": ["Pitfall description", ...],
|
|
164
|
+
"updatedVerification": "Updated verification step",
|
|
165
|
+
"confidence": 0.0-1.0
|
|
166
|
+
}
|
|
167
|
+
If no meaningful improvements can be extracted, respond with: {"not_applicable": true}`,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
role: "user",
|
|
171
|
+
content: `## Current Skill
|
|
172
|
+
${skillContent.slice(0, 1500)}
|
|
173
|
+
|
|
174
|
+
## Better Trajectory (score: ${betterTrajectory.outcomeScore || "?"})
|
|
175
|
+
Intent: ${betterTrajectory.userIntent || "unknown"}
|
|
176
|
+
Tool chain:
|
|
177
|
+
${toolSteps}`,
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── SkillImprover class ────────────────────────────────
|
|
183
|
+
|
|
184
|
+
export class SkillImprover {
|
|
185
|
+
/**
|
|
186
|
+
* @param {import("better-sqlite3").Database} db
|
|
187
|
+
* @param {function} llmChat — async (messages) => string
|
|
188
|
+
* @param {import("./trajectory-store.js").TrajectoryStore} trajectoryStore
|
|
189
|
+
* @param {{skillsDir?:string}} [config]
|
|
190
|
+
*/
|
|
191
|
+
constructor(db, llmChat, trajectoryStore, config = {}) {
|
|
192
|
+
this.db = db;
|
|
193
|
+
this.llmChat = llmChat;
|
|
194
|
+
this.trajectoryStore = trajectoryStore;
|
|
195
|
+
this.skillsDir = config.skillsDir || null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Repair a skill after an execution error.
|
|
200
|
+
* @param {string} skillName
|
|
201
|
+
* @param {object} errorContext — { error, toolChain, userIntent }
|
|
202
|
+
* @returns {Promise<{improved:boolean, reason:string}>}
|
|
203
|
+
*/
|
|
204
|
+
async repairFromError(skillName, errorContext) {
|
|
205
|
+
const skillContent = await this._readSkill(skillName);
|
|
206
|
+
if (!skillContent) {
|
|
207
|
+
return { improved: false, reason: "skill not found" };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const suggestion = await this._callLLM(
|
|
211
|
+
buildRepairPrompt(skillContent, errorContext),
|
|
212
|
+
);
|
|
213
|
+
if (!suggestion || suggestion.not_applicable) {
|
|
214
|
+
return { improved: false, reason: "LLM deemed not applicable" };
|
|
215
|
+
}
|
|
216
|
+
if ((suggestion.confidence || 0) < 0.4) {
|
|
217
|
+
return { improved: false, reason: "low confidence" };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const { meta, body } = parseSkillFrontmatter(skillContent);
|
|
221
|
+
const newBody = this._applyProcedurePatch(body, suggestion);
|
|
222
|
+
meta.version = bumpVersion(meta.version);
|
|
223
|
+
|
|
224
|
+
const newContent = rebuildSkillMd(meta, newBody);
|
|
225
|
+
await this._writeSkill(skillName, newContent);
|
|
226
|
+
this._logImprovement(skillName, "error_repair", suggestion.diagnosis || "");
|
|
227
|
+
|
|
228
|
+
return { improved: true, reason: suggestion.diagnosis || "repaired" };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Update a skill based on user correction.
|
|
233
|
+
* @param {string} skillName
|
|
234
|
+
* @param {object} correctionContext — { userMessage, previousToolChain, correctedToolChain }
|
|
235
|
+
* @returns {Promise<{improved:boolean, reason:string}>}
|
|
236
|
+
*/
|
|
237
|
+
async updateFromCorrection(skillName, correctionContext) {
|
|
238
|
+
const skillContent = await this._readSkill(skillName);
|
|
239
|
+
if (!skillContent) {
|
|
240
|
+
return { improved: false, reason: "skill not found" };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const suggestion = await this._callLLM(
|
|
244
|
+
buildCorrectionPrompt(skillContent, correctionContext),
|
|
245
|
+
);
|
|
246
|
+
if (!suggestion || suggestion.not_applicable) {
|
|
247
|
+
return { improved: false, reason: "LLM deemed not applicable" };
|
|
248
|
+
}
|
|
249
|
+
if ((suggestion.confidence || 0) < 0.4) {
|
|
250
|
+
return { improved: false, reason: "low confidence" };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const { meta, body } = parseSkillFrontmatter(skillContent);
|
|
254
|
+
const newBody = this._applyCorrectionPatch(body, suggestion);
|
|
255
|
+
meta.version = bumpVersion(meta.version);
|
|
256
|
+
|
|
257
|
+
const newContent = rebuildSkillMd(meta, newBody);
|
|
258
|
+
await this._writeSkill(skillName, newContent);
|
|
259
|
+
this._logImprovement(
|
|
260
|
+
skillName,
|
|
261
|
+
"user_correction",
|
|
262
|
+
suggestion.whatChanged || "",
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
return { improved: true, reason: suggestion.whatChanged || "corrected" };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Improve a skill from a higher-scoring trajectory.
|
|
270
|
+
* @param {string} skillName
|
|
271
|
+
* @param {object} betterTrajectory — hydrated trajectory object
|
|
272
|
+
* @returns {Promise<{improved:boolean, reason:string}>}
|
|
273
|
+
*/
|
|
274
|
+
async improveFromBetterTrajectory(skillName, betterTrajectory) {
|
|
275
|
+
const skillContent = await this._readSkill(skillName);
|
|
276
|
+
if (!skillContent) {
|
|
277
|
+
return { improved: false, reason: "skill not found" };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const suggestion = await this._callLLM(
|
|
281
|
+
buildImprovementPrompt(skillContent, betterTrajectory),
|
|
282
|
+
);
|
|
283
|
+
if (!suggestion || suggestion.not_applicable) {
|
|
284
|
+
return { improved: false, reason: "LLM deemed not applicable" };
|
|
285
|
+
}
|
|
286
|
+
if ((suggestion.confidence || 0) < 0.4) {
|
|
287
|
+
return { improved: false, reason: "low confidence" };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const { meta, body } = parseSkillFrontmatter(skillContent);
|
|
291
|
+
const newBody = this._applyImprovementPatch(body, suggestion);
|
|
292
|
+
meta.version = bumpVersion(meta.version);
|
|
293
|
+
|
|
294
|
+
const newContent = rebuildSkillMd(meta, newBody);
|
|
295
|
+
await this._writeSkill(skillName, newContent);
|
|
296
|
+
this._logImprovement(
|
|
297
|
+
skillName,
|
|
298
|
+
"better_trajectory",
|
|
299
|
+
suggestion.improvements || "",
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
return { improved: true, reason: suggestion.improvements || "improved" };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Scan for skills that can be improved from recent high-score trajectories.
|
|
307
|
+
* @returns {Promise<{improved: string[], skipped: string[]}>}
|
|
308
|
+
*/
|
|
309
|
+
async scanForImprovements() {
|
|
310
|
+
const improved = [];
|
|
311
|
+
const skipped = [];
|
|
312
|
+
|
|
313
|
+
// Find synthesized trajectories that have higher-scoring siblings
|
|
314
|
+
const synthesized = this.db
|
|
315
|
+
.prepare(
|
|
316
|
+
`SELECT DISTINCT synthesized_skill, tool_chain, outcome_score
|
|
317
|
+
FROM learning_trajectories
|
|
318
|
+
WHERE synthesized_skill IS NOT NULL
|
|
319
|
+
ORDER BY outcome_score ASC
|
|
320
|
+
LIMIT 20`,
|
|
321
|
+
)
|
|
322
|
+
.all();
|
|
323
|
+
|
|
324
|
+
for (const row of synthesized) {
|
|
325
|
+
try {
|
|
326
|
+
let chain;
|
|
327
|
+
try {
|
|
328
|
+
chain = JSON.parse(row.tool_chain);
|
|
329
|
+
} catch {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const toolNames = [...new Set(chain.map((t) => t.tool))];
|
|
334
|
+
const betterOnes = this.trajectoryStore.findSimilar(toolNames, {
|
|
335
|
+
minSimilarity: 0.6,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Find a trajectory with significantly higher score
|
|
339
|
+
const better = betterOnes.find(
|
|
340
|
+
(t) =>
|
|
341
|
+
t.outcomeScore != null &&
|
|
342
|
+
t.outcomeScore > (row.outcome_score || 0) + 0.15,
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
if (better) {
|
|
346
|
+
const result = await this.improveFromBetterTrajectory(
|
|
347
|
+
row.synthesized_skill,
|
|
348
|
+
better,
|
|
349
|
+
);
|
|
350
|
+
if (result.improved) {
|
|
351
|
+
improved.push(row.synthesized_skill);
|
|
352
|
+
} else {
|
|
353
|
+
skipped.push(`${row.synthesized_skill}: ${result.reason}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} catch (err) {
|
|
357
|
+
skipped.push(
|
|
358
|
+
`${row.synthesized_skill || "unknown"}: error - ${err.message}`,
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return { improved, skipped };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ── Internal ────────────────────────────────────────
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Call LLM and parse JSON response.
|
|
370
|
+
* @param {Array<{role:string, content:string}>} messages
|
|
371
|
+
* @returns {Promise<object|null>}
|
|
372
|
+
*/
|
|
373
|
+
async _callLLM(messages) {
|
|
374
|
+
if (!this.llmChat) return null;
|
|
375
|
+
try {
|
|
376
|
+
const response = await this.llmChat(messages);
|
|
377
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
378
|
+
if (!jsonMatch) return null;
|
|
379
|
+
return JSON.parse(jsonMatch[0]);
|
|
380
|
+
} catch {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Read a skill file from disk.
|
|
387
|
+
* @param {string} skillName
|
|
388
|
+
* @returns {Promise<string|null>}
|
|
389
|
+
*/
|
|
390
|
+
async _readSkill(skillName) {
|
|
391
|
+
if (!this.skillsDir) return null;
|
|
392
|
+
const skillFile = _deps.path.join(this.skillsDir, skillName, "SKILL.md");
|
|
393
|
+
try {
|
|
394
|
+
return await _deps.fs.promises.readFile(skillFile, "utf-8");
|
|
395
|
+
} catch {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Write updated skill content to disk.
|
|
402
|
+
* @param {string} skillName
|
|
403
|
+
* @param {string} content
|
|
404
|
+
*/
|
|
405
|
+
async _writeSkill(skillName, content) {
|
|
406
|
+
if (!this.skillsDir) return;
|
|
407
|
+
const skillDir = _deps.path.join(this.skillsDir, skillName);
|
|
408
|
+
const skillFile = _deps.path.join(skillDir, "SKILL.md");
|
|
409
|
+
await _deps.fs.promises.mkdir(skillDir, { recursive: true });
|
|
410
|
+
await _deps.fs.promises.writeFile(skillFile, content, "utf-8");
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Log an improvement to skill_improvement_log table.
|
|
415
|
+
* @param {string} skillName
|
|
416
|
+
* @param {string} triggerType
|
|
417
|
+
* @param {string} detail
|
|
418
|
+
*/
|
|
419
|
+
_logImprovement(skillName, triggerType, detail) {
|
|
420
|
+
try {
|
|
421
|
+
this.db
|
|
422
|
+
.prepare(
|
|
423
|
+
`INSERT INTO skill_improvement_log (skill_name, trigger_type, detail)
|
|
424
|
+
VALUES (?, ?, ?)`,
|
|
425
|
+
)
|
|
426
|
+
.run(skillName, triggerType, (detail || "").slice(0, 500));
|
|
427
|
+
} catch {
|
|
428
|
+
// Non-critical, don't break the flow
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Apply repair patch to skill body (replace Procedure + append Pitfalls).
|
|
434
|
+
* @param {string} body
|
|
435
|
+
* @param {object} suggestion
|
|
436
|
+
* @returns {string}
|
|
437
|
+
*/
|
|
438
|
+
_applyProcedurePatch(body, suggestion) {
|
|
439
|
+
let result = body;
|
|
440
|
+
|
|
441
|
+
if (suggestion.fixedProcedure && suggestion.fixedProcedure.length > 0) {
|
|
442
|
+
const newProcedure = suggestion.fixedProcedure
|
|
443
|
+
.map((step, i) => `${i + 1}. ${step}`)
|
|
444
|
+
.join("\n");
|
|
445
|
+
result = result.replace(
|
|
446
|
+
/## Procedure\n[\s\S]*?(?=\n## |\n$|$)/,
|
|
447
|
+
`## Procedure\n${newProcedure}`,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (suggestion.newPitfalls && suggestion.newPitfalls.length > 0) {
|
|
452
|
+
const pitfallLines = suggestion.newPitfalls
|
|
453
|
+
.map((p) => `- ${p}`)
|
|
454
|
+
.join("\n");
|
|
455
|
+
result = result.replace(
|
|
456
|
+
/## Pitfalls\n[\s\S]*?(?=\n## |\n$|$)/,
|
|
457
|
+
`## Pitfalls\n${pitfallLines}`,
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Apply correction patch to skill body.
|
|
466
|
+
* @param {string} body
|
|
467
|
+
* @param {object} suggestion
|
|
468
|
+
* @returns {string}
|
|
469
|
+
*/
|
|
470
|
+
_applyCorrectionPatch(body, suggestion) {
|
|
471
|
+
let result = body;
|
|
472
|
+
|
|
473
|
+
if (suggestion.updatedProcedure && suggestion.updatedProcedure.length > 0) {
|
|
474
|
+
const newProcedure = suggestion.updatedProcedure
|
|
475
|
+
.map((step, i) => `${i + 1}. ${step}`)
|
|
476
|
+
.join("\n");
|
|
477
|
+
result = result.replace(
|
|
478
|
+
/## Procedure\n[\s\S]*?(?=\n## |\n$|$)/,
|
|
479
|
+
`## Procedure\n${newProcedure}`,
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (suggestion.newPitfalls && suggestion.newPitfalls.length > 0) {
|
|
484
|
+
const pitfallLines = suggestion.newPitfalls
|
|
485
|
+
.map((p) => `- ${p}`)
|
|
486
|
+
.join("\n");
|
|
487
|
+
result = result.replace(
|
|
488
|
+
/## Pitfalls\n[\s\S]*?(?=\n## |\n$|$)/,
|
|
489
|
+
`## Pitfalls\n${pitfallLines}`,
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return result;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Apply improvement patch to skill body.
|
|
498
|
+
* @param {string} body
|
|
499
|
+
* @param {object} suggestion
|
|
500
|
+
* @returns {string}
|
|
501
|
+
*/
|
|
502
|
+
_applyImprovementPatch(body, suggestion) {
|
|
503
|
+
let result = body;
|
|
504
|
+
|
|
505
|
+
if (suggestion.mergedProcedure && suggestion.mergedProcedure.length > 0) {
|
|
506
|
+
const newProcedure = suggestion.mergedProcedure
|
|
507
|
+
.map((step, i) => `${i + 1}. ${step}`)
|
|
508
|
+
.join("\n");
|
|
509
|
+
result = result.replace(
|
|
510
|
+
/## Procedure\n[\s\S]*?(?=\n## |\n$|$)/,
|
|
511
|
+
`## Procedure\n${newProcedure}`,
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (suggestion.mergedPitfalls && suggestion.mergedPitfalls.length > 0) {
|
|
516
|
+
const pitfallLines = suggestion.mergedPitfalls
|
|
517
|
+
.map((p) => `- ${p}`)
|
|
518
|
+
.join("\n");
|
|
519
|
+
result = result.replace(
|
|
520
|
+
/## Pitfalls\n[\s\S]*?(?=\n## |\n$|$)/,
|
|
521
|
+
`## Pitfalls\n${pitfallLines}`,
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (suggestion.updatedVerification) {
|
|
526
|
+
result = result.replace(
|
|
527
|
+
/## Verification\n[\s\S]*?(?=\n## |\n$|$)/,
|
|
528
|
+
`## Verification\n${suggestion.updatedVerification}`,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return result;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export { _deps };
|