@lmzhen/dsh-evolution-commands 0.2.1 → 0.3.0

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 CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
2
- import { appendEvolutionEvent, buildLearnPrompt, eventsFile } from "@lmzhen/dsh-evolution-core";
2
+ import { SkillLibrary, appendEvolutionEvent, buildLearnPrompt, eventsFile, parseFrontmatter, usageObserved } from "@lmzhen/dsh-evolution-core";
3
+ import { buildMaintainFacts, runMaintain, snapshotFromLibrary } from "@lmzhen/dsh-evolution-maintenance";
3
4
  import { homedir } from "node:os";
4
5
  import { join } from "node:path";
5
6
  //#region lib/types/index.js
@@ -8,13 +9,40 @@ import { join } from "node:path";
8
9
  * @module @lmzhen/dsh-evolution-commands
9
10
  */
10
11
  const name = "evolution-commands";
11
- function apply(ctx) {
12
+ async function buildEnrichment(ctx, library) {
13
+ const skillUsage = ctx.get("skillUsage");
14
+ const usageMap = skillUsage?.report ? await skillUsage.report() : void 0;
15
+ const usageObservedValue = usageMap ? usageObserved(usageMap) : void 0;
16
+ const descriptions = /* @__PURE__ */ new Map();
17
+ const supportFiles = /* @__PURE__ */ new Map();
18
+ const quality = /* @__PURE__ */ new Map();
19
+ for (const entry of await library.list()) {
20
+ const body = await library.read(entry.name);
21
+ if (body === null) continue;
22
+ const description = parseFrontmatter(body)?.frontmatter.description;
23
+ if (typeof description === "string" && description.trim().length > 0) descriptions.set(entry.name, description);
24
+ const files = await library.listSupportFiles(entry.name);
25
+ if (files.length > 0) supportFiles.set(entry.name, files);
26
+ const record = usageMap?.get(entry.name);
27
+ if (typeof record?.quality_score === "number") quality.set(entry.name, record.quality_score);
28
+ }
29
+ return {
30
+ descriptions,
31
+ supportFiles,
32
+ quality,
33
+ usageObservedValue
34
+ };
35
+ }
36
+ function apply(ctx, rawConfig = {}) {
37
+ const config = rawConfig;
38
+ let lastMaintainAt = 0;
39
+ let lastMaintainRunId = "";
12
40
  ctx.inject(["commands"], (commandCtx) => {
13
41
  commandCtx.commands.register({
14
42
  name: "evolution",
15
43
  description: "Self-evolution status and approval controls",
16
44
  recordInput: false,
17
- 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] | replay" },
45
+ 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 | restructure <name> \"<heading>\" <to_file> | replay" },
18
46
  async handler(invocation) {
19
47
  const input = invocation.rawInput?.trim() ?? "";
20
48
  const ok = (text) => ({
@@ -115,14 +143,17 @@ function apply(ctx) {
115
143
  return result.ok ? ok(result.message) : err(result.message);
116
144
  }
117
145
  if (input.startsWith("consolidate ")) {
118
- const [target, ...sources] = input.slice(12).trim().split(/\s+/).filter(Boolean);
146
+ const planTail = /\s--plan\s+(\S+)\s*$/.exec(input) ?? null;
147
+ const planRunId = planTail?.[1] ?? void 0;
148
+ const [target, ...sources] = (planTail ? input.slice(0, planTail.index) : input).slice(12).trim().split(/\s+/).filter(Boolean);
119
149
  if (!target || sources.length === 0) return err("Usage: /evolution consolidate <target> <source...>");
120
150
  const curator = ctx.get("evolutionCurator");
121
151
  const result = curator ? await curator.consolidate(target, sources) : {
122
152
  ok: false,
123
153
  message: "Curator service not mounted."
124
154
  };
125
- return result.ok ? ok(result.message) : err(result.message);
155
+ if (!result.ok) return err(result.message);
156
+ return ok(planRunId ? `${result.message}\n[audit] plan=${planRunId}` : result.message);
126
157
  }
127
158
  if (input.startsWith("skill restore ")) {
128
159
  const name = input.slice(14).trim();
@@ -167,12 +198,77 @@ function apply(ctx) {
167
198
  });
168
199
  return ok("Learning request sent to this session. Follow it now.");
169
200
  }
201
+ if (input === "maintain --facts") {
202
+ const ioRegistry = ctx.get("evolutionIo");
203
+ if (!ioRegistry) return err("Evolution IO registry not mounted — maintenance facts unavailable.");
204
+ const library = new SkillLibrary(config.skillsRoot, ioRegistry.provider());
205
+ const enrichment = await buildEnrichment(ctx, library);
206
+ const { facts } = buildMaintainFacts(await snapshotFromLibrary(library, {
207
+ descriptions: enrichment.descriptions,
208
+ supportFiles: enrichment.supportFiles,
209
+ quality: enrichment.quality
210
+ }), enrichment.usageObservedValue, void 0);
211
+ return ok(`Maintenance facts (0-token preview):\n${facts}`);
212
+ }
213
+ if (input === "maintain") {
214
+ const cooldownMs = config.maintainCooldownMs ?? 13e4;
215
+ const sinceLast = Date.now() - lastMaintainAt;
216
+ if (cooldownMs > 0 && sinceLast < cooldownMs) return ok(`Maintenance cooldown active (${Math.ceil((cooldownMs - sinceLast) / 1e3)}s) — latest scan ${lastMaintainRunId}; re-running now would spend another model call.`);
217
+ const ioRegistry = ctx.get("evolutionIo");
218
+ const subagents = ctx.get("subagents");
219
+ if (!ioRegistry) return err("Evolution IO registry not mounted — maintenance scan unavailable.");
220
+ if (!subagents) return err("Subagents service not mounted — maintenance scan unavailable.");
221
+ const library = new SkillLibrary(config.skillsRoot, ioRegistry.provider());
222
+ const enrichment = await buildEnrichment(ctx, library);
223
+ const outcome = await runMaintain({
224
+ library,
225
+ subagents,
226
+ parent: invocation.agent
227
+ }, {
228
+ descriptions: () => enrichment.descriptions,
229
+ supportFiles: () => enrichment.supportFiles,
230
+ quality: () => enrichment.quality,
231
+ usageObserved: () => enrichment.usageObservedValue
232
+ });
233
+ lastMaintainAt = Date.now();
234
+ lastMaintainRunId = outcome.runId ?? "";
235
+ if (!outcome.ok) return err(outcome.error ?? "Maintenance scan failed.");
236
+ appendEvolutionEvent(ioRegistry.provider(), eventsFile(process.env.DSH_HOME ?? join(homedir(), ".dsh")), {
237
+ type: "maintain",
238
+ source: "manual",
239
+ runId: outcome.runId,
240
+ verdict: outcome.verdict,
241
+ recommendations: outcome.text?.split("\n").filter((line) => line.startsWith("- [")).length ?? 0
242
+ }).catch((error) => {
243
+ ctx.logger.warn(`evolution-commands: failed to record maintain event: ${String(error)}`);
244
+ });
245
+ return ok(`Maintenance scan ${outcome.runId}:\n${outcome.text ?? ""}`);
246
+ }
247
+ if (input.startsWith("restructure ")) {
248
+ const planTail = /\s--plan\s+(\S+)\s*$/.exec(input) ?? null;
249
+ const planRunId = planTail?.[1] ?? void 0;
250
+ const rest = planTail ? input.slice(0, planTail.index) : input;
251
+ const match = /^restructure\s+(\S+)\s+"([^"]+)"\s+(\S+)$/.exec(rest);
252
+ if (!match) return err("Usage: /evolution restructure <name> \"<## heading>\" <to_file>");
253
+ const name = match[1] ?? "";
254
+ const heading = match[2] ?? "";
255
+ const toFile = match[3] ?? "";
256
+ if (!toFile.startsWith("references/")) return err("to_file must live under references/ (log/detail destination).");
257
+ const ioRegistry = ctx.get("evolutionIo");
258
+ if (!ioRegistry) return err("Evolution IO registry not mounted — restructure unavailable.");
259
+ const result = await new SkillLibrary(config.skillsRoot, ioRegistry.provider()).restructure(name, [{
260
+ heading,
261
+ toFile
262
+ }], "foreground");
263
+ if (!result.ok) return err(result.message);
264
+ return ok(planRunId ? `${result.message}\n[audit] plan=${planRunId}` : result.message);
265
+ }
170
266
  if (input === "replay") {
171
267
  const replay = ctx.get("evolutionReplay");
172
268
  if (!replay) return err("Replay service not mounted.");
173
269
  return ok(replay.compare().report);
174
270
  }
175
- 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] | replay.");
271
+ 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 | restructure <name> \"<heading>\" <to_file> | replay.");
176
272
  }
177
273
  });
178
274
  });
@@ -4,5 +4,15 @@
4
4
  */
5
5
  import type { Context } from '@deepseek-ai/cordis';
6
6
  export declare const name = "evolution-commands";
7
- export declare function apply(ctx: Context): void;
7
+ export interface Config {
8
+ /** Skill-tree root for maintain/restructure; empty uses skillsRoot().
9
+ * Align with tool-skill-manage/skill-usage/evolution-skill-catalog rows (A7). */
10
+ skillsRoot?: string | undefined;
11
+ /** Cooldown window for scan commands (ms) — misclick/rapid-trigger guard;
12
+ * secondary calls inside the window return the previous runId instead of
13
+ * spending another model call. Default 130s (>= maintain timeout 120s, so
14
+ * the window also covers in-flight runs); transient (per-process). */
15
+ maintainCooldownMs?: number | undefined;
16
+ }
17
+ export declare function apply(ctx: Context, rawConfig?: Config): void;
8
18
  //# 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.2.1",
4
+ "version": "0.3.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -33,7 +33,8 @@
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
35
  "@deepseek-ai/schemastery": "^3.18.1",
36
- "@lmzhen/dsh-evolution-core": "^0.2.1"
36
+ "@lmzhen/dsh-evolution-core": "^0.3.0",
37
+ "@lmzhen/dsh-evolution-maintenance": "^0.3.0"
37
38
  },
38
39
  "peerDependencies": {
39
40
  "@deepseek-ai/cordis": "^4.0.1",