@lmzhen/dsh-evolution-commands 0.3.66 → 0.3.68

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
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { effectiveSessionPolicy } from "@lmzhen/dsh-evolution-approval";
4
4
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
5
- import { SkillLibrary, appendEvolutionEvent, buildLearnPrompt, clampedNumber, composePresetComposition, eventsFile, evolutionRoot, resolveRootConfig, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
5
+ import { MAX_TIMER_DELAY_MS, SkillLibrary, appendEvolutionEvent, assertSkillsRootAliasRetired, buildLearnPrompt, clampedNumber, composePresetComposition, eventsFile, evolutionRoot, resolveRootConfig, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
6
6
  import { buildEnrichment, buildMaintainFacts, runMaintain, snapshotFromLibrary } from "@lmzhen/dsh-evolution-maintenance";
7
7
  import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
8
8
  import { dirname, join } from "node:path";
@@ -12,7 +12,8 @@ import { fileURLToPath } from "node:url";
12
12
  * `/evolution doctor` — read-only self-check (0.3.55, WB2).
13
13
  *
14
14
  * Turns "what the README would explain" into "the tool tells you directly":
15
- * install form, three-way conflict detection (all/host/preset + layered), the
15
+ * install form, three-way conflict detection (all/host/preset + layered,
16
+ * including preset-bundle × layered via the preset's delivered agent.cordis.yml), the
16
17
  * environment surfaces that bit us before (v10 P2-12/13), and which evolution
17
18
  * services are actually mounted in this runtime. Always ends with suggested
18
19
  * actions so any finding carries its next step.
@@ -62,13 +63,14 @@ async function diagnose(ctx, options = {}) {
62
63
  const full = bundles.some((name) => tailOf(name) === "dsh-evolution-all");
63
64
  const host = bundles.some((name) => tailOf(name) === "dsh-evolution-host");
64
65
  const preset = bundles.some((name) => tailOf(name) === "dsh-evolution-preset");
65
- const presetDir = join(home, ".agent-presets", "evolution");
66
- const layered = host && existsSync(presetDir);
66
+ const presetDirInstalled = existsSync(join(join(home, ".agent-presets", "evolution"), "agent.cordis.yml"));
67
+ const layered = host && presetDirInstalled;
67
68
  const conflicts = [];
68
69
  if (full && host) conflicts.push("evolution-all and evolution-host are installed together — the infra rows double-mount and startup fails loud. Keep ONE: remove the other bundle.");
69
70
  if (full && preset) conflicts.push("evolution-all and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
70
71
  if (host && preset) conflicts.push("evolution-host and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
71
- if (full && layered) conflicts.push("evolution-all and the layered Evolution preset are both present — the model rows double-mount. Keep ONE (use layered without all, or drop the preset).");
72
+ if (full && presetDirInstalled) conflicts.push("evolution-all and the layered Evolution preset are both present — the model rows double-mount. Keep ONE (use layered without all, or drop the preset).");
73
+ if (preset && presetDirInstalled) conflicts.push("evolution-preset and the layered Evolution preset are both present — the model rows double-mount (the preset bundle carries the same model rows as all). Keep ONE (drop the preset bundle, or remove the layered preset).");
72
74
  const services = {
73
75
  review: full || host || preset,
74
76
  curator: has("evolutionCurator"),
@@ -187,7 +189,7 @@ const COMMAND_ENTRIES = [
187
189
  },
188
190
  {
189
191
  usage: "replay",
190
- summary: "compare prompt-bundle replay for this session"
192
+ summary: "compare plan outcomes across sessions and restarts (backfilled from the activity store)"
191
193
  }
192
194
  ];
193
195
  /** The input-declaration hint (single line, ` | `-separated). */
@@ -215,9 +217,8 @@ const Config = z.object({
215
217
  });
216
218
  function apply(ctx, rawConfig = {}) {
217
219
  const config = rawConfig;
218
- const rootConfig = resolveRootConfig(rawConfig);
219
- if (rootConfig.usedDeprecatedAlias) ctx.logger.warn("evolution-commands: config \"skillsRoot\" is deprecated (E-7); use \"root\" — the alias is honoured until 0.3.65");
220
- const skillsRootValue = rootConfig.root;
220
+ assertSkillsRootAliasRetired(rawConfig);
221
+ const skillsRootValue = resolveRootConfig(rawConfig).root;
221
222
  let lastMaintainAt = 0;
222
223
  let lastMaintainRunId = "";
223
224
  let maintainInFlightSince = 0;
@@ -229,7 +230,8 @@ function apply(ctx, rawConfig = {}) {
229
230
  recordInput: false,
230
231
  input: { hint: renderHint() },
231
232
  async handler(invocation) {
232
- const input = invocation.rawInput?.trim() ?? "";
233
+ const rawInputTrimmed = invocation.rawInput.trim();
234
+ const input = rawInputTrimmed.replace(/\s+/g, " ");
233
235
  const ok = (text) => ({
234
236
  kind: "success",
235
237
  text
@@ -381,8 +383,8 @@ function apply(ctx, rawConfig = {}) {
381
383
  return ok("Skill catalog refresh requested: caches dropped, catalog re-read on next lookup.");
382
384
  }
383
385
  if (input === "learn" || input.startsWith("learn ")) {
384
- const request = input === "learn" ? "" : input.slice(6).trim();
385
- invocation.agent.inject(createUserMessage({
386
+ const request = rawInputTrimmed === "learn" ? "" : rawInputTrimmed.slice(6).trim();
387
+ const message = createUserMessage({
386
388
  content: [{
387
389
  type: "text",
388
390
  text: buildLearnPrompt(request)
@@ -393,7 +395,11 @@ function apply(ctx, rawConfig = {}) {
393
395
  form: "notice",
394
396
  summary: "learn request"
395
397
  }
396
- }));
398
+ });
399
+ const followup = invocation.agent.followup;
400
+ const woke = typeof followup === "function";
401
+ if (woke) followup(message);
402
+ else invocation.agent.inject(message);
397
403
  const eventIo = ctx.get("evolutionIo")?.provider();
398
404
  if (eventIo) appendEvolutionEvent(eventIo, eventsFile(evolutionRoot()), {
399
405
  type: "learn",
@@ -402,7 +408,7 @@ function apply(ctx, rawConfig = {}) {
402
408
  }).catch((error) => {
403
409
  ctx.logger.warn(`evolution-commands: failed to record learn event: ${String(error)}`);
404
410
  });
405
- return ok("Learning request sent to this session. Follow it now.");
411
+ return woke ? ok("Learning request sent to this session. Follow it now.") : ok("Learning request queued for this session — this host exposes no wake-up channel, so it is read on your next message.");
406
412
  }
407
413
  if (input === "maintain --facts") {
408
414
  const ioRegistry = ctx.get("evolutionIo");
@@ -421,7 +427,7 @@ function apply(ctx, rawConfig = {}) {
421
427
  const maintainArgs = /^maintain(?:\s+--timeout[ =](\d+))?\s*$/.exec(input);
422
428
  if (maintainArgs) {
423
429
  const runTimeoutMs = maintainArgs[1] ? Number(maintainArgs[1]) : config.maintainTimeoutMs ?? 6e5;
424
- 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.");
430
+ if (!Number.isSafeInteger(runTimeoutMs) || runTimeoutMs <= 0 || runTimeoutMs > MAX_TIMER_DELAY_MS) return err(maintainArgs[1] ? `Invalid --timeout value: expected a positive integer number of milliseconds up to ${MAX_TIMER_DELAY_MS} (e.g. /evolution maintain --timeout 600000).` : `The maintainTimeoutMs config is invalid: expected a positive integer number of milliseconds up to ${MAX_TIMER_DELAY_MS}. Fix the evolution-commands row config (maintainTimeoutMs), then retry.`);
425
431
  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.`);
426
432
  const cooldownMs = clampedNumber(config.maintainCooldownMs, 3e4, { min: 0 });
427
433
  const sinceLast = Date.now() - lastMaintainAt;
@@ -442,7 +448,9 @@ function apply(ctx, rawConfig = {}) {
442
448
  tools: { get: (name) => ctx.get("tools")?.get(name) },
443
449
  logger: { warn: (message) => {
444
450
  ctx.logger.warn(message);
445
- } }
451
+ } },
452
+ rootExists: () => ioRegistry.provider().exists(skillsRootValue),
453
+ skillRoot: skillsRootValue
446
454
  }, {
447
455
  timeoutMs: runTimeoutMs,
448
456
  signal: invocation.signal,
@@ -499,9 +507,9 @@ function apply(ctx, rawConfig = {}) {
499
507
  }
500
508
  }
501
509
  if (input.startsWith("restructure ")) {
502
- const planTail = /\s--plan\s+(\S+)\s*$/.exec(input) ?? null;
510
+ const planTail = /\s--plan\s+(\S+)\s*$/.exec(rawInputTrimmed) ?? null;
503
511
  const planRunId = planTail?.[1] ?? void 0;
504
- const rest = planTail ? input.slice(0, planTail.index) : input;
512
+ const rest = planTail ? rawInputTrimmed.slice(0, planTail.index) : rawInputTrimmed;
505
513
  const match = /^restructure\s+(\S+)\s+"([^"]+)"\s+(\S+)$/.exec(rest);
506
514
  if (!match) return err("Usage: /evolution restructure <name> \"<## heading>\" <to_file>");
507
515
  const name = match[1] ?? "";
@@ -511,7 +519,8 @@ function apply(ctx, rawConfig = {}) {
511
519
  const ioRegistry = ctx.get("evolutionIo");
512
520
  if (!ioRegistry) return err("Evolution IO registry not mounted — restructure unavailable.");
513
521
  if (approval) {
514
- const sessionPolicy = effectiveSessionPolicy(ctx, void 0);
522
+ const session = invocation.agent.session;
523
+ const sessionPolicy = effectiveSessionPolicy(ctx, session);
515
524
  if (approval.isEnabled !== false && sessionPolicy !== "never" && approval.stageForeground !== false && !approval.hasRunner("skill")) return err("Restructure cannot be staged: no skill replay runner is registered — mount the tool-skill-manage row (evolution-agent preset, or evolution-all) or disable evolution-approval.");
516
525
  const decision = await approval.request({
517
526
  kind: "skill",
@@ -528,7 +537,10 @@ function apply(ctx, rawConfig = {}) {
528
537
  origin: "foreground",
529
538
  libraryOrigin: "foreground"
530
539
  },
531
- origin: "foreground"
540
+ origin: "foreground",
541
+ ...session.id ? { sessionId: session.id } : {},
542
+ session,
543
+ ...sessionPolicy !== void 0 ? { sessionPolicy } : {}
532
544
  });
533
545
  if (decision.action === "staged") return ok(`${decision.message}${planRunId ? `\n[audit] plan=${planRunId}` : ""}`);
534
546
  }
@@ -11,8 +11,9 @@ export interface Config {
11
11
  * resolved through resolveSkillsRoot() so an empty/whitespace value falls
12
12
  * back to the default instead of a CWD-relative root. */
13
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. */
14
+ /** V27 G2.4 (M-08): RETIRED alias of `root`. The field stays declared so the
15
+ * loader can hand it to the load-time gate (which rejects it loudly) instead
16
+ * of dropping it silently; the plugin never reads it as a root. */
16
17
  skillsRoot?: string | undefined;
17
18
  /** Cooldown window for scan commands (ms) — misclick/rapid-trigger guard;
18
19
  * secondary calls inside the window return the previous runId instead of
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.66",
4
+ "version": "0.3.68",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,19 +31,21 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-approval": "^0.3.66",
35
- "@lmzhen/dsh-evolution-core": "^0.3.66",
36
- "@lmzhen/dsh-evolution-maintenance": "^0.3.66"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.68",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.68",
36
+ "@lmzhen/dsh-evolution-maintenance": "^0.3.68"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@lmzhen/dsh-evolution-agent-preset": "^0.3.66"
39
+ "@lmzhen/dsh-evolution-agent-preset": "^0.3.68"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@deepseek-ai/cordis": "^4.0.1",
43
+ "@deepseek-ai/dsh-commands": "^0.1.1-rc.2",
43
44
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
44
45
  "@deepseek-ai/dsh-llm": "^0.1.1-rc.2"
45
46
  },
46
47
  "devDependencies": {
48
+ "@deepseek-ai/dsh-commands": "^0.1.1-rc.2",
47
49
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2"
48
50
  }
49
51
  }