@lmzhen/dsh-evolution-commands 0.3.63 → 0.3.65

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,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
4
- import { SkillLibrary, appendEvolutionEvent, buildLearnPrompt, clampedNumber, composePresetComposition, eventsFile, evolutionRoot, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
4
+ import { SkillLibrary, appendEvolutionEvent, buildLearnPrompt, clampedNumber, composePresetComposition, eventsFile, evolutionRoot, resolveRootConfig, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
5
5
  import { buildEnrichment, buildMaintainFacts, runMaintain, snapshotFromLibrary } from "@lmzhen/dsh-evolution-maintenance";
6
6
  import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
7
7
  import { dirname, join } from "node:path";
@@ -16,6 +16,14 @@ import { fileURLToPath } from "node:url";
16
16
  * services are actually mounted in this runtime. Always ends with suggested
17
17
  * actions so any finding carries its next step.
18
18
  */
19
+ /** D-6 (v18): exact-segment tail match (the loose substring form matched a
20
+ * hypothetical `dsh-evolution-allowlist`). */
21
+ const tailOf = (name) => name.slice(name.lastIndexOf("/") + 1);
22
+ const EVOLUTION_BUNDLE_TAILS = new Set([
23
+ "dsh-evolution-all",
24
+ "dsh-evolution-host",
25
+ "dsh-evolution-preset"
26
+ ]);
19
27
  /** Profile bundle rows for evolution-family packages across all profiles. */
20
28
  function collectEvolutionBundles(home) {
21
29
  const profilesDir = join(home, "profiles");
@@ -25,7 +33,7 @@ function collectEvolutionBundles(home) {
25
33
  if (!profile.isDirectory()) continue;
26
34
  try {
27
35
  const manifest = JSON.parse(readFileSync(join(profilesDir, profile.name, "package.json"), "utf8"));
28
- for (const name of manifest.dsh?.profile?.bundles ?? []) if (/evolution-(all|host|preset)/.test(name)) bundles.push(name);
36
+ for (const name of manifest.dsh?.profile?.bundles ?? []) if (EVOLUTION_BUNDLE_TAILS.has(tailOf(name))) bundles.push(name);
29
37
  } catch {}
30
38
  }
31
39
  return bundles;
@@ -50,9 +58,9 @@ async function diagnose(ctx, options = {}) {
50
58
  const home = options.home ?? evolutionRoot();
51
59
  const bundles = collectEvolutionBundles(home);
52
60
  const has = (name) => ctx.get(name) !== void 0;
53
- const full = bundles.some((name) => /evolution-all/.test(name));
54
- const host = bundles.some((name) => /evolution-host/.test(name));
55
- const preset = bundles.some((name) => /evolution-preset/.test(name));
61
+ const full = bundles.some((name) => tailOf(name) === "dsh-evolution-all");
62
+ const host = bundles.some((name) => tailOf(name) === "dsh-evolution-host");
63
+ const preset = bundles.some((name) => tailOf(name) === "dsh-evolution-preset");
56
64
  const presetDir = join(home, ".agent-presets", "evolution");
57
65
  const layered = host && existsSync(presetDir);
58
66
  const conflicts = [];
@@ -191,6 +199,7 @@ function renderHelpText() {
191
199
  /** Enrichment maps shared by the full scan and the `--facts` preview (v12). */
192
200
  const name = "evolution-commands";
193
201
  const Config = z.object({
202
+ root: z.string().default(""),
194
203
  skillsRoot: z.string().default(""),
195
204
  maintainCooldownMs: z.number().min(0).default(3e4),
196
205
  maintainTimeoutMs: z.number().min(1).default(6e5),
@@ -198,6 +207,9 @@ const Config = z.object({
198
207
  });
199
208
  function apply(ctx, rawConfig = {}) {
200
209
  const config = rawConfig;
210
+ const rootConfig = resolveRootConfig(rawConfig);
211
+ if (rootConfig.usedDeprecatedAlias) ctx.logger.warn("evolution-commands: config \"skillsRoot\" is deprecated (E-7); use \"root\" — the alias is honoured until 0.3.65");
212
+ const skillsRootValue = rootConfig.root;
201
213
  let lastMaintainAt = 0;
202
214
  let lastMaintainRunId = "";
203
215
  let maintainInFlightSince = 0;
@@ -251,6 +263,7 @@ function apply(ctx, rawConfig = {}) {
251
263
  const curator = ctx.get("evolutionCurator");
252
264
  if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
253
265
  const result = await curator.run({ ignoreGates: true });
266
+ if (result.skipped !== void 0) return ok(`Curator run skipped (${result.skipped}): no curation pass was executed.\nrunId=${result.report.runId}`);
254
267
  return ok(`Curator run complete: ${result.stale.length} stale, ${result.archived.length} archived, ${result.errors.length} failed.\nrunId=${result.report.runId}${result.report.snapshotPath ? `\nsnapshot=${result.report.snapshotPath}` : ""}`);
255
268
  }
256
269
  if (input === "curator pause" || input === "curator resume") {
@@ -314,6 +327,8 @@ function apply(ctx, rawConfig = {}) {
314
327
  ].join("\n"));
315
328
  }
316
329
  if (input === "restore" || input.startsWith("restore ")) {
330
+ const tail = input.slice(7).trim();
331
+ if (tail !== "") return err(`\`restore\` takes no arguments (got "${tail}"). Use \`restore\` for a whole-tree rollback, or \`skill restore <name>\` for one skill.`);
317
332
  const curator = ctx.get("evolutionCurator");
318
333
  const result = curator ? await curator.restoreSnapshot() : {
319
334
  ok: false,
@@ -384,7 +399,7 @@ function apply(ctx, rawConfig = {}) {
384
399
  if (input === "maintain --facts") {
385
400
  const ioRegistry = ctx.get("evolutionIo");
386
401
  if (!ioRegistry) return err("Evolution IO registry not mounted — maintenance facts unavailable.");
387
- const library = new SkillLibrary(resolveSkillsRoot({ root: config.skillsRoot }), ioRegistry.provider());
402
+ const library = new SkillLibrary(resolveSkillsRoot({ root: skillsRootValue }), ioRegistry.provider());
388
403
  const enrichment = await buildEnrichment(ctx, library);
389
404
  const { facts } = buildMaintainFacts(await snapshotFromLibrary(library, {
390
405
  descriptions: enrichment.descriptions,
@@ -398,7 +413,7 @@ function apply(ctx, rawConfig = {}) {
398
413
  const maintainArgs = /^maintain(?:\s+--timeout[ =](\d+))?\s*$/.exec(input);
399
414
  if (maintainArgs) {
400
415
  const runTimeoutMs = maintainArgs[1] ? Number(maintainArgs[1]) : config.maintainTimeoutMs ?? 6e5;
401
- if (!Number.isSafeInteger(runTimeoutMs) || runTimeoutMs <= 0 || runTimeoutMs > 4294967295) return err(maintainArgs[1] ? "Invalid --timeout value: expected a positive integer number of milliseconds up to 4294967295 (e.g. /evolution maintain --timeout 600000)." : "The maintainTimeoutMs config is invalid: expected a positive integer number of milliseconds up to 4294967295. Fix the evolution-commands row config (maintainTimeoutMs), then retry.");
416
+ if (!Number.isSafeInteger(runTimeoutMs) || runTimeoutMs <= 0 || runTimeoutMs > 2147483647) return err(maintainArgs[1] ? "Invalid --timeout value: expected a positive integer number of milliseconds up to 2147483647 (e.g. /evolution maintain --timeout 600000)." : "The maintainTimeoutMs config is invalid: expected a positive integer number of milliseconds up to 2147483647. Fix the evolution-commands row config (maintainTimeoutMs), then retry.");
402
417
  if (maintainInFlightSince > 0) return err(`Maintenance scan is already running (since ~${Math.max(1, Math.round((Date.now() - maintainInFlightSince) / 1e3))}s ago) — re-submitting now would cancel it. Wait for it to settle; the result appears when it finishes.`);
403
418
  const cooldownMs = clampedNumber(config.maintainCooldownMs, 3e4, { min: 0 });
404
419
  const sinceLast = Date.now() - lastMaintainAt;
@@ -409,7 +424,7 @@ function apply(ctx, rawConfig = {}) {
409
424
  if (!subagents) return err("Subagents service not mounted — maintenance scan unavailable.");
410
425
  try {
411
426
  maintainInFlightSince = Date.now();
412
- const library = new SkillLibrary(resolveSkillsRoot({ root: config.skillsRoot }), ioRegistry.provider());
427
+ const library = new SkillLibrary(resolveSkillsRoot({ root: skillsRootValue }), ioRegistry.provider());
413
428
  const enrichment = await buildEnrichment(ctx, library);
414
429
  const outcome = await runMaintain({
415
430
  library,
@@ -422,6 +437,7 @@ function apply(ctx, rawConfig = {}) {
422
437
  } }
423
438
  }, {
424
439
  timeoutMs: runTimeoutMs,
440
+ signal: invocation.signal,
425
441
  descriptions: () => enrichment.descriptions,
426
442
  supportFiles: () => enrichment.supportFiles,
427
443
  quality: () => enrichment.quality,
@@ -486,7 +502,7 @@ function apply(ctx, rawConfig = {}) {
486
502
  if (!toFile.startsWith("references/")) return err("to_file must live under references/ (log/detail destination).");
487
503
  const ioRegistry = ctx.get("evolutionIo");
488
504
  if (!ioRegistry) return err("Evolution IO registry not mounted — restructure unavailable.");
489
- const result = await new SkillLibrary(resolveSkillsRoot({ root: config.skillsRoot }), ioRegistry.provider(), void 0, (event) => {
505
+ const result = await new SkillLibrary(resolveSkillsRoot({ root: skillsRootValue }), ioRegistry.provider(), void 0, (event) => {
490
506
  ctx.emit("evolution/skill-mutated", event);
491
507
  }, void 0, config.threatExemptLabels ?? []).restructure(name, [{
492
508
  heading,
@@ -520,14 +536,12 @@ function apply(ctx, rawConfig = {}) {
520
536
  * dir≠name trap), so BOTH sibling shapes are probed.
521
537
  */
522
538
  function resolveAgentPresetDir(importMetaUrl) {
523
- const dir = dirname(fileURLToPath(importMetaUrl));
524
- const siblingCandidate = join(dir, "..", "dsh-evolution-agent-preset");
525
- const candidates = [siblingCandidate, join(dir, "..", "..", "evolution-agent")];
526
- for (const candidate of candidates) if (existsSync(join(candidate, "agent.cordis.yml")) && existsSync(join(candidate, "preset.yml"))) return candidate;
539
+ const overlayCandidate = join(dirname(fileURLToPath(importMetaUrl)), "..", "..", "evolution-agent");
540
+ if (existsSync(join(overlayCandidate, "agent.cordis.yml")) && existsSync(join(overlayCandidate, "preset.yml"))) return overlayCandidate;
527
541
  try {
528
542
  return dirname(createRequire(importMetaUrl).resolve("@lmzhen/dsh-evolution-agent-preset/package.json"));
529
543
  } catch {
530
- return siblingCandidate;
544
+ return overlayCandidate;
531
545
  }
532
546
  }
533
547
  const defaultFs = {
@@ -6,10 +6,13 @@ import type { Context } from '@deepseek-ai/cordis';
6
6
  import z from '@deepseek-ai/schemastery';
7
7
  export declare const name = "evolution-commands";
8
8
  export interface Config {
9
- /** Skill-tree root for maintain/restructure; empty uses skillsRoot().
10
- * Align with tool-skill-manage/skill-usage/evolution-skill-catalog rows (A7).
11
- * 0.3.22 (G3.2, F-342): resolved through resolveSkillsRoot() so an empty/
12
- * whitespace value falls back to the default instead of a CWD-relative root. */
9
+ /** Skill-tree root for maintain/restructure; empty uses the default tree.
10
+ * E-7 (v18): canonical key — the same `root` every other family row reads,
11
+ * resolved through resolveSkillsRoot() so an empty/whitespace value falls
12
+ * back to the default instead of a CWD-relative root. */
13
+ root?: string | undefined;
14
+ /** Deprecated alias of `root` (E-7, v18): honoured only while `root` is
15
+ * empty, with a warning; removed after 0.3.65. */
13
16
  skillsRoot?: string | undefined;
14
17
  /** Cooldown window for scan commands (ms) — misclick/rapid-trigger guard;
15
18
  * secondary calls inside the window return the previous runId instead of
@@ -34,11 +37,13 @@ export interface Config {
34
37
  threatExemptLabels?: string[] | undefined;
35
38
  }
36
39
  export declare const Config: z<Schemastery.ObjectS<{
40
+ root: z<string, string>;
37
41
  skillsRoot: z<string, string>;
38
42
  maintainCooldownMs: z<number, number>;
39
43
  maintainTimeoutMs: z<number, number>;
40
44
  threatExemptLabels: z<string[], string[]>;
41
45
  }>, Schemastery.ObjectT<{
46
+ root: z<string, string>;
42
47
  skillsRoot: z<string, string>;
43
48
  maintainCooldownMs: z<number, number>;
44
49
  maintainTimeoutMs: z<number, number>;
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 (/evolution pending|curator|maintain|doctor) (community build)",
4
- "version": "0.3.63",
4
+ "version": "0.3.65",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,12 +31,12 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-approval": "^0.3.63",
35
- "@lmzhen/dsh-evolution-core": "^0.3.63",
36
- "@lmzhen/dsh-evolution-maintenance": "^0.3.63"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.65",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.65",
36
+ "@lmzhen/dsh-evolution-maintenance": "^0.3.65"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@lmzhen/dsh-evolution-agent-preset": "^0.3.63"
39
+ "@lmzhen/dsh-evolution-agent-preset": "^0.3.65"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@deepseek-ai/cordis": "^4.0.1",