@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 +32 -20
- package/lib/types/index.d.ts +3 -2
- package/package.json +7 -5
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
|
|
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
|
|
66
|
-
const layered = host &&
|
|
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 &&
|
|
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
|
|
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
|
-
|
|
219
|
-
|
|
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
|
|
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 =
|
|
385
|
-
|
|
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 >
|
|
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(
|
|
510
|
+
const planTail = /\s--plan\s+(\S+)\s*$/.exec(rawInputTrimmed) ?? null;
|
|
503
511
|
const planRunId = planTail?.[1] ?? void 0;
|
|
504
|
-
const rest = planTail ?
|
|
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
|
|
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
|
}
|
package/lib/types/index.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
15
|
-
*
|
|
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.
|
|
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.
|
|
35
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
36
|
-
"@lmzhen/dsh-evolution-maintenance": "^0.3.
|
|
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.
|
|
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
|
}
|