@lmzhen/dsh-evolution-commands 0.3.17 → 0.3.18
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/lib/index.js +73 -13
- package/lib/types/index.d.ts +14 -1
- package/package.json +3 -3
package/lib/index.js
CHANGED
|
@@ -2,13 +2,13 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { SkillLibrary, appendEvolutionEvent, buildLearnPrompt, composePresetComposition, eventsFile } from "@lmzhen/dsh-evolution-core";
|
|
4
4
|
import { buildEnrichment, buildMaintainFacts, runMaintain, snapshotFromLibrary } from "@lmzhen/dsh-evolution-maintenance";
|
|
5
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
//#region lib/types/index.js
|
|
10
10
|
/**
|
|
11
|
-
* Human commands for the evolution family: /evolution learn|pending|curator|restore|consolidate.
|
|
11
|
+
* Human commands for the evolution family: /evolution learn|pending|curator|restore|consolidate|skills refresh.
|
|
12
12
|
* @module @lmzhen/dsh-evolution-commands
|
|
13
13
|
*/
|
|
14
14
|
/** Enrichment maps shared by the full scan and the `--facts` preview (v12). */
|
|
@@ -19,11 +19,12 @@ function apply(ctx, rawConfig = {}) {
|
|
|
19
19
|
let lastMaintainRunId = "";
|
|
20
20
|
let maintainInFlightSince = 0;
|
|
21
21
|
ctx.inject(["commands"], (commandCtx) => {
|
|
22
|
-
commandCtx.commands
|
|
22
|
+
const commands = commandCtx.commands;
|
|
23
|
+
commandCtx.effect(() => commands.register({
|
|
23
24
|
name: "evolution",
|
|
24
25
|
description: "Self-evolution status and approval controls",
|
|
25
26
|
recordInput: false,
|
|
26
|
-
input: { hint: "pending | approve <id> | reject <id> | curator run|pause|resume|status|report|scope | restore | consolidate <target> <sources...> | skill restore <name> | skills health | learn [request] | maintain [--timeout ms] | preset install | restructure <name> \"<heading>\" <to_file> | replay" },
|
|
27
|
+
input: { hint: "pending | approve <id> | reject <id> | curator run|pause|resume|status|report|scope | mutations | restore | consolidate <target> <sources...> | skill restore <name> | skills health | skills refresh | learn [request] | maintain [--timeout ms | --facts] | preset install | restructure <name> \"<heading>\" <to_file> | replay" },
|
|
27
28
|
async handler(invocation) {
|
|
28
29
|
const input = invocation.rawInput?.trim() ?? "";
|
|
29
30
|
const ok = (text) => ({
|
|
@@ -156,6 +157,10 @@ function apply(ctx, rawConfig = {}) {
|
|
|
156
157
|
if (rows.length === 0) return ok(banner ? `Structure health: all skills healthy. ${banner}` : "Structure health: all skills healthy.");
|
|
157
158
|
return ok([banner ? `Structure health (${rows.length} degraded): ${banner}` : `Structure health (${rows.length} degraded):`, ...rows.map(line)].join("\n"));
|
|
158
159
|
}
|
|
160
|
+
if (input === "skills refresh") {
|
|
161
|
+
ctx.emit("evolution/skills-refresh");
|
|
162
|
+
return ok("Skill catalog refresh requested: caches dropped, catalog re-read on next lookup.");
|
|
163
|
+
}
|
|
159
164
|
if (input === "learn" || input.startsWith("learn ")) {
|
|
160
165
|
const request = input === "learn" ? "" : input.slice(6).trim();
|
|
161
166
|
invocation.agent.inject(createUserMessage({
|
|
@@ -213,7 +218,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
213
218
|
const outcome = await runMaintain({
|
|
214
219
|
library,
|
|
215
220
|
subagents,
|
|
216
|
-
parent: invocation.agent
|
|
221
|
+
parent: invocation.agent,
|
|
222
|
+
evolutionPolicy: { get: () => ctx.get("evolutionPolicy")?.get() }
|
|
217
223
|
}, {
|
|
218
224
|
timeoutMs: runTimeoutMs,
|
|
219
225
|
descriptions: () => enrichment.descriptions,
|
|
@@ -230,7 +236,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
230
236
|
source: "manual",
|
|
231
237
|
runId: outcome.runId,
|
|
232
238
|
verdict: outcome.verdict,
|
|
233
|
-
recommendations: outcome.text
|
|
239
|
+
recommendations: countMaintainRecommendations(outcome.text)
|
|
234
240
|
}).catch((error) => {
|
|
235
241
|
ctx.logger.warn(`evolution-commands: failed to record maintain event: ${String(error)}`);
|
|
236
242
|
});
|
|
@@ -254,8 +260,13 @@ function apply(ctx, rawConfig = {}) {
|
|
|
254
260
|
if (!registry) return err("Agent preset registry not mounted — cannot compose the Evolution preset against the runtime standard.");
|
|
255
261
|
const composition = composePresetComposition(await registry.read("standard"), readFileSync(deltaPath, "utf8"));
|
|
256
262
|
mkdirSync(target, { recursive: true });
|
|
257
|
-
|
|
258
|
-
|
|
263
|
+
atomicWriteFiles(target, [{
|
|
264
|
+
name: "agent.cordis.yml",
|
|
265
|
+
content: composition
|
|
266
|
+
}, {
|
|
267
|
+
name: "preset.yml",
|
|
268
|
+
content: readFileSync(presetPath)
|
|
269
|
+
}]);
|
|
259
270
|
return ok(`Evolution agent preset installed to ${target} (runtime standard + delta). Restart the session switcher to select it.`);
|
|
260
271
|
} catch (error) {
|
|
261
272
|
return err(`Preset install failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -285,9 +296,9 @@ function apply(ctx, rawConfig = {}) {
|
|
|
285
296
|
if (!replay) return err("Replay service not mounted.");
|
|
286
297
|
return ok(replay.compare().report);
|
|
287
298
|
}
|
|
288
|
-
return ok("Evolution: memory, skills, review, curator. Use /evolution pending | approve <id> | reject <id> | curator run | curator status | curator pause | curator resume | curator report | curator scope | restore | consolidate <target> <source...> | skill restore <name> | skills health | learn [request] | maintain | preset install | restructure <name> \"<heading>\" <to_file> | replay.");
|
|
299
|
+
return ok("Evolution: memory, skills, review, curator. Use /evolution pending | approve <id> | reject <id> | curator run | curator status | curator pause | curator resume | curator report | curator scope | mutations | restore | consolidate <target> <source...> | skill restore <name> | skills health | skills refresh | learn [request] | maintain [--timeout ms | --facts] | preset install | restructure <name> \"<heading>\" <to_file> | replay.");
|
|
289
300
|
}
|
|
290
|
-
});
|
|
301
|
+
}));
|
|
291
302
|
});
|
|
292
303
|
}
|
|
293
304
|
/**
|
|
@@ -300,13 +311,62 @@ function apply(ctx, rawConfig = {}) {
|
|
|
300
311
|
*/
|
|
301
312
|
function resolveAgentPresetDir(importMetaUrl) {
|
|
302
313
|
const dir = dirname(fileURLToPath(importMetaUrl));
|
|
303
|
-
const
|
|
314
|
+
const siblingCandidate = join(dir, "..", "dsh-evolution-agent-preset");
|
|
315
|
+
const candidates = [siblingCandidate, join(dir, "..", "..", "evolution-agent")];
|
|
304
316
|
for (const candidate of candidates) if (existsSync(join(candidate, "agent.cordis.yml")) && existsSync(join(candidate, "preset.yml"))) return candidate;
|
|
305
317
|
try {
|
|
306
318
|
return dirname(createRequire(importMetaUrl).resolve("@lmzhen/dsh-evolution-agent-preset/package.json"));
|
|
307
319
|
} catch {
|
|
308
|
-
return
|
|
320
|
+
return siblingCandidate;
|
|
309
321
|
}
|
|
310
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* S6.3 (E-40): atomically replace a set of files within one directory.
|
|
325
|
+
* Each file is staged to a sibling `<name>.tmp`, then renamed into place
|
|
326
|
+
* (atomic on the same filesystem); a pre-existing file keeps a single
|
|
327
|
+
* `<name>.bak`. A failure at ANY staging step removes the staged temps and
|
|
328
|
+
* leaves the previous files untouched — a half-written target is impossible.
|
|
329
|
+
*/
|
|
330
|
+
function atomicWriteFiles(targetDir, writes) {
|
|
331
|
+
const stage = (name) => join(targetDir, `${name}.tmp`);
|
|
332
|
+
try {
|
|
333
|
+
for (const { name, content } of writes) writeFileSync(stage(name), content);
|
|
334
|
+
for (const { name } of writes) {
|
|
335
|
+
const finalPath = join(targetDir, name);
|
|
336
|
+
const bakPath = join(targetDir, `${name}.bak`);
|
|
337
|
+
if (existsSync(finalPath) && !existsSync(bakPath)) copyFileSync(finalPath, bakPath);
|
|
338
|
+
}
|
|
339
|
+
for (const { name } of writes) {
|
|
340
|
+
const finalPath = join(targetDir, name);
|
|
341
|
+
try {
|
|
342
|
+
renameSync(stage(name), finalPath);
|
|
343
|
+
} catch {
|
|
344
|
+
rmSync(finalPath, { force: true });
|
|
345
|
+
renameSync(stage(name), finalPath);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
for (const { name } of writes) rmSync(stage(name), {
|
|
350
|
+
force: true,
|
|
351
|
+
recursive: true
|
|
352
|
+
});
|
|
353
|
+
throw error;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Count the recommendation bullets in a rendered maintain plan.
|
|
358
|
+
* `formatPlan` renders each recommendation as a line starting with `- [<kind>]`;
|
|
359
|
+
* this matches that bullet (anchored at line start) instead of any string
|
|
360
|
+
* prefix, so a `- note` line can never inflate the count.
|
|
361
|
+
*
|
|
362
|
+
* @param text - the rendered maintain result text, or `undefined`.
|
|
363
|
+
* @returns the number of recommendation bullets in `text`.
|
|
364
|
+
* @remarks the count is still text-derived — MaintainOutcome does not expose the
|
|
365
|
+
* structured plan array, so a structured count would require a
|
|
366
|
+
* evolution-maintenance outcome change (out of this package's scope).
|
|
367
|
+
*/
|
|
368
|
+
function countMaintainRecommendations(text) {
|
|
369
|
+
return text?.match(/^- \[/gm)?.length ?? 0;
|
|
370
|
+
}
|
|
311
371
|
//#endregion
|
|
312
|
-
export { apply, name };
|
|
372
|
+
export { apply, countMaintainRecommendations, name };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Human commands for the evolution family: /evolution learn|pending|curator|restore|consolidate.
|
|
2
|
+
* Human commands for the evolution family: /evolution learn|pending|curator|restore|consolidate|skills refresh.
|
|
3
3
|
* @module @deepseek-ai/dsh-evolution-commands
|
|
4
4
|
*/
|
|
5
5
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -22,4 +22,17 @@ export interface Config {
|
|
|
22
22
|
maintainTimeoutMs?: number | undefined;
|
|
23
23
|
}
|
|
24
24
|
export declare function apply(ctx: Context, rawConfig?: Config): void;
|
|
25
|
+
/**
|
|
26
|
+
* Count the recommendation bullets in a rendered maintain plan.
|
|
27
|
+
* `formatPlan` renders each recommendation as a line starting with `- [<kind>]`;
|
|
28
|
+
* this matches that bullet (anchored at line start) instead of any string
|
|
29
|
+
* prefix, so a `- note` line can never inflate the count.
|
|
30
|
+
*
|
|
31
|
+
* @param text - the rendered maintain result text, or `undefined`.
|
|
32
|
+
* @returns the number of recommendation bullets in `text`.
|
|
33
|
+
* @remarks the count is still text-derived — MaintainOutcome does not expose the
|
|
34
|
+
* structured plan array, so a structured count would require a
|
|
35
|
+
* evolution-maintenance outcome change (out of this package's scope).
|
|
36
|
+
*/
|
|
37
|
+
export declare function countMaintainRecommendations(text: string | undefined): number;
|
|
25
38
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-commands",
|
|
3
3
|
"description": "Human commands for the evolution family (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.18",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
36
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
37
|
-
"@lmzhen/dsh-evolution-maintenance": "^0.3.
|
|
36
|
+
"@lmzhen/dsh-evolution-core": "^0.3.18",
|
|
37
|
+
"@lmzhen/dsh-evolution-maintenance": "^0.3.18"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"@deepseek-ai/cordis": "^4.0.1",
|