@lmzhen/dsh-evolution-commands 0.3.53 → 0.3.55
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 +185 -3
- package/lib/types/doctor.d.ts +29 -0
- package/lib/types/registry.d.ts +23 -0
- package/package.json +6 -6
package/lib/index.js
CHANGED
|
@@ -2,9 +2,186 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
3
3
|
import { SkillLibrary, appendEvolutionEvent, buildLearnPrompt, clampedNumber, composePresetComposition, eventsFile, evolutionRoot, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
|
|
4
4
|
import { buildEnrichment, buildMaintainFacts, runMaintain, snapshotFromLibrary } from "@lmzhen/dsh-evolution-maintenance";
|
|
5
|
-
import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
//#region lib/types/doctor.js
|
|
9
|
+
/**
|
|
10
|
+
* `/evolution doctor` — read-only self-check (0.3.55, WB2).
|
|
11
|
+
*
|
|
12
|
+
* Turns "what the README would explain" into "the tool tells you directly":
|
|
13
|
+
* install form, three-way conflict detection (all/host/preset + layered), the
|
|
14
|
+
* environment surfaces that bit us before (v10 P2-12/13), and which evolution
|
|
15
|
+
* services are actually mounted in this runtime. Always ends with suggested
|
|
16
|
+
* actions so any finding carries its next step.
|
|
17
|
+
*/
|
|
18
|
+
/** Profile bundle rows for evolution-family packages across all profiles. */
|
|
19
|
+
function collectEvolutionBundles(home) {
|
|
20
|
+
const profilesDir = join(home, "profiles");
|
|
21
|
+
if (!existsSync(profilesDir)) return [];
|
|
22
|
+
const bundles = [];
|
|
23
|
+
for (const profile of readdirSync(profilesDir, { withFileTypes: true })) {
|
|
24
|
+
if (!profile.isDirectory()) continue;
|
|
25
|
+
try {
|
|
26
|
+
const manifest = JSON.parse(readFileSync(join(profilesDir, profile.name, "package.json"), "utf8"));
|
|
27
|
+
for (const name of manifest.dsh?.profile?.bundles ?? []) if (/evolution-(all|host|preset)/.test(name)) bundles.push(name);
|
|
28
|
+
} catch {}
|
|
29
|
+
}
|
|
30
|
+
return bundles;
|
|
31
|
+
}
|
|
32
|
+
const SESSION_QUERY_MODES = new Set([
|
|
33
|
+
"startup",
|
|
34
|
+
"first-search",
|
|
35
|
+
"never"
|
|
36
|
+
]);
|
|
37
|
+
function envIssues() {
|
|
38
|
+
const issues = [];
|
|
39
|
+
const query = process.env.DSH_EVOLUTION_SESSION_QUERY;
|
|
40
|
+
if (query && !SESSION_QUERY_MODES.has(query)) issues.push(`DSH_EVOLUTION_SESSION_QUERY="${query}" is not one of startup|first-search|never — the patch normalizes it to 'startup' silently; set it to a listed mode.`);
|
|
41
|
+
return issues;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Diagnose the deployment. `ctx` supplies service presence (a bare stub with
|
|
45
|
+
* only `get` is enough); `home` defaults to the evolution root so tests can
|
|
46
|
+
* point doctor at a temp DSH_HOME.
|
|
47
|
+
*/
|
|
48
|
+
async function diagnose(ctx, options = {}) {
|
|
49
|
+
const home = options.home ?? evolutionRoot();
|
|
50
|
+
const bundles = collectEvolutionBundles(home);
|
|
51
|
+
const has = (name) => ctx.get(name) !== void 0;
|
|
52
|
+
const full = bundles.some((name) => /evolution-all/.test(name));
|
|
53
|
+
const host = bundles.some((name) => /evolution-host/.test(name));
|
|
54
|
+
const preset = bundles.some((name) => /evolution-preset/.test(name));
|
|
55
|
+
const presetDir = join(home, ".agent-presets", "evolution");
|
|
56
|
+
const layered = host && existsSync(presetDir);
|
|
57
|
+
const conflicts = [];
|
|
58
|
+
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.");
|
|
59
|
+
if (full && preset) conflicts.push("evolution-all and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
|
|
60
|
+
if (host && preset) conflicts.push("evolution-host and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
|
|
61
|
+
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).");
|
|
62
|
+
const services = {
|
|
63
|
+
review: has("evolutionReview"),
|
|
64
|
+
curator: has("evolutionCurator"),
|
|
65
|
+
approval: has("evolutionApproval"),
|
|
66
|
+
skillUsage: has("skillUsage"),
|
|
67
|
+
io: has("evolutionIo")
|
|
68
|
+
};
|
|
69
|
+
let pendingCount = null;
|
|
70
|
+
const approvalService = ctx.get("evolutionApproval");
|
|
71
|
+
if (approvalService?.list) try {
|
|
72
|
+
const rows = await approvalService.list("pending");
|
|
73
|
+
pendingCount = Array.isArray(rows) ? rows.length : null;
|
|
74
|
+
} catch {
|
|
75
|
+
pendingCount = null;
|
|
76
|
+
}
|
|
77
|
+
const installForm = full ? "full" : preset ? "preset" : layered ? "layered" : host ? "host" : "none";
|
|
78
|
+
const actions = [];
|
|
79
|
+
if (conflicts.length > 0) actions.push("Resolve the conflict first: keep exactly one of evolution-all / evolution-host / evolution-preset / layered.");
|
|
80
|
+
else if (installForm === "none") actions.push("Install the default full bundle: dsh plugin --profile web add @lmzhen/dsh-evolution-all");
|
|
81
|
+
else if (installForm === "layered") actions.push("Model tools follow the Evolution preset per session; add @lmzhen/dsh-evolution-all instead if every session should have them.");
|
|
82
|
+
const env = envIssues();
|
|
83
|
+
if (env.length > 0) actions.push("Fix the DSH_EVOLUTION_* variable listed above.");
|
|
84
|
+
if (services.review && !services.curator) actions.push("Curator service is not mounted — automatic curation is off; verify the host/all bundle row set is complete.");
|
|
85
|
+
if (pendingCount === null && services.approval) actions.push("Approval service is mounted but pending listing failed — check the evolution state service.");
|
|
86
|
+
return {
|
|
87
|
+
installForm,
|
|
88
|
+
bundles,
|
|
89
|
+
conflicts,
|
|
90
|
+
envIssues: env,
|
|
91
|
+
services,
|
|
92
|
+
pendingCount,
|
|
93
|
+
actions
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function renderDoctorText(report) {
|
|
97
|
+
const lines = [
|
|
98
|
+
`Evolution doctor — install form: ${report.installForm}`,
|
|
99
|
+
`bundles: ${report.bundles.length > 0 ? report.bundles.join(", ") : "(none)"}`,
|
|
100
|
+
`services: review=${report.services.review} curator=${report.services.curator} approval=${report.services.approval} skillUsage=${report.services.skillUsage} io=${report.services.io}`,
|
|
101
|
+
`pending: ${report.pendingCount === null ? "unknown" : report.pendingCount}`
|
|
102
|
+
];
|
|
103
|
+
if (report.conflicts.length > 0) lines.push("conflicts:", ...report.conflicts.map((line) => ` ! ${line}`));
|
|
104
|
+
if (report.envIssues.length > 0) lines.push("env:", ...report.envIssues.map((line) => ` ! ${line}`));
|
|
105
|
+
if (report.actions.length > 0) lines.push("next steps:", ...report.actions.map((line) => ` → ${line}`));
|
|
106
|
+
return lines.join("\n");
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region lib/types/registry.js
|
|
110
|
+
const COMMAND_ENTRIES = [
|
|
111
|
+
{
|
|
112
|
+
usage: "pending [--detail]",
|
|
113
|
+
summary: "list staged evolution writes (--detail shows staged args)"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
usage: "approve <id>",
|
|
117
|
+
summary: "replay an approved staged write through its runner"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
usage: "reject <id>",
|
|
121
|
+
summary: "drop a staged write without running it"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
usage: "doctor",
|
|
125
|
+
summary: "read-only self-check: install form, conflicts, env, services"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
usage: "curator run|pause|resume|status|report|scope",
|
|
129
|
+
summary: "run one curation pass, control or inspect automatic curation"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
usage: "mutations",
|
|
133
|
+
summary: "list skill-mutation audit records"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
usage: "restore",
|
|
137
|
+
summary: "restore skills from the latest snapshot"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
usage: "consolidate <target> <sources...>",
|
|
141
|
+
summary: "merge source skills into a target umbrella skill"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
usage: "skill restore <name>",
|
|
145
|
+
summary: "restore one archived skill by name"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
usage: "skills health",
|
|
149
|
+
summary: "structure-health verdicts for the skill library"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
usage: "skills refresh",
|
|
153
|
+
summary: "drop the catalog caches and re-read the tree"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
usage: "learn [request]",
|
|
157
|
+
summary: "send a learning request to this session"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
usage: "maintain [--timeout=<ms> | --facts]",
|
|
161
|
+
summary: "run a maintenance scan (--facts: 0-token preview)"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
usage: "preset install",
|
|
165
|
+
summary: "generate the Evolution agent preset into the user root"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
usage: "restructure <name> \"<heading>\" <to_file>",
|
|
169
|
+
summary: "move a body section into a references/ file"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
usage: "replay",
|
|
173
|
+
summary: "compare prompt-bundle replay for this session"
|
|
174
|
+
}
|
|
175
|
+
];
|
|
176
|
+
/** The input-declaration hint (single line, ` | `-separated). */
|
|
177
|
+
function renderHint() {
|
|
178
|
+
return COMMAND_ENTRIES.map((entry) => entry.usage).join(" | ");
|
|
179
|
+
}
|
|
180
|
+
/** The bare `/evolution` help body (one line per subcommand). */
|
|
181
|
+
function renderHelpText() {
|
|
182
|
+
return COMMAND_ENTRIES.map((entry) => ` ${entry.usage.padEnd(44)} ${entry.summary}`).join("\n");
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
8
185
|
//#region lib/types/index.js
|
|
9
186
|
/**
|
|
10
187
|
* Human commands for the evolution family: /evolution learn|pending|curator|restore|consolidate|skills refresh.
|
|
@@ -23,7 +200,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
23
200
|
name: "evolution",
|
|
24
201
|
description: "Self-evolution status and approval controls",
|
|
25
202
|
recordInput: false,
|
|
26
|
-
input: { hint:
|
|
203
|
+
input: { hint: renderHint() },
|
|
27
204
|
async handler(invocation) {
|
|
28
205
|
const input = invocation.rawInput?.trim() ?? "";
|
|
29
206
|
const ok = (text) => ({
|
|
@@ -315,7 +492,12 @@ function apply(ctx, rawConfig = {}) {
|
|
|
315
492
|
if (!replay) return err("Replay service not mounted.");
|
|
316
493
|
return ok(replay.compare().report);
|
|
317
494
|
}
|
|
318
|
-
|
|
495
|
+
if (input === "doctor" || input === "doctor --json") {
|
|
496
|
+
const report = await diagnose(ctx, { home: evolutionRoot() });
|
|
497
|
+
if (input === "doctor --json") return ok(JSON.stringify(report, null, 2));
|
|
498
|
+
return ok(renderDoctorText(report));
|
|
499
|
+
}
|
|
500
|
+
return ok(`Evolution: memory, skills, review, curator — self-evolution status, approval and maintenance.\n${renderHelpText()}`);
|
|
319
501
|
}
|
|
320
502
|
}));
|
|
321
503
|
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface DoctorReport {
|
|
2
|
+
installForm: 'full' | 'host' | 'preset' | 'layered' | 'none';
|
|
3
|
+
bundles: string[];
|
|
4
|
+
conflicts: string[];
|
|
5
|
+
envIssues: string[];
|
|
6
|
+
services: {
|
|
7
|
+
review: boolean;
|
|
8
|
+
curator: boolean;
|
|
9
|
+
approval: boolean;
|
|
10
|
+
skillUsage: boolean;
|
|
11
|
+
io: boolean;
|
|
12
|
+
};
|
|
13
|
+
pendingCount: number | null;
|
|
14
|
+
actions: string[];
|
|
15
|
+
}
|
|
16
|
+
/** Profile bundle rows for evolution-family packages across all profiles. */
|
|
17
|
+
export declare function collectEvolutionBundles(home: string): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Diagnose the deployment. `ctx` supplies service presence (a bare stub with
|
|
20
|
+
* only `get` is enough); `home` defaults to the evolution root so tests can
|
|
21
|
+
* point doctor at a temp DSH_HOME.
|
|
22
|
+
*/
|
|
23
|
+
export declare function diagnose(ctx: {
|
|
24
|
+
get(name: string): unknown;
|
|
25
|
+
}, options?: {
|
|
26
|
+
home?: string;
|
|
27
|
+
}): Promise<DoctorReport>;
|
|
28
|
+
export declare function renderDoctorText(report: DoctorReport): string;
|
|
29
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subcommand registry — the SINGLE SOURCE for the /evolution command surface.
|
|
3
|
+
*
|
|
4
|
+
* 0.3.55 (WD1): the input-declaration hint, the bare `/evolution` help output
|
|
5
|
+
* and the README command table all render FROM this table; a hand-written
|
|
6
|
+
* copy anywhere is drift waiting to happen (v10 R-13 proved it once).
|
|
7
|
+
* Keep `usage` in the exact shape a user can type after `/evolution`.
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandEntry {
|
|
10
|
+
/** The subcommand as typed after `/evolution`. Variants use `|` (curator run|pause). */
|
|
11
|
+
usage: string;
|
|
12
|
+
/** One-line purpose rendered in the help output. */
|
|
13
|
+
summary: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const COMMAND_ENTRIES: CommandEntry[];
|
|
16
|
+
/** The input-declaration hint (single line, ` | `-separated). */
|
|
17
|
+
export declare function renderHint(): string;
|
|
18
|
+
/** The bare `/evolution` help body (one line per subcommand). */
|
|
19
|
+
export declare function renderHelpText(): string;
|
|
20
|
+
/** Markdown command table fragment for README surfaces (T-WD2 pins it).
|
|
21
|
+
* `|` inside a usage (curator run|pause|…) is escaped for the table cell. */
|
|
22
|
+
export declare function renderCommandTable(): string;
|
|
23
|
+
//# sourceMappingURL=registry.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-commands",
|
|
3
|
-
"description": "Human commands for the evolution family (community build)",
|
|
4
|
-
"version": "0.3.
|
|
3
|
+
"description": "Human commands for the evolution family (/evolution pending|curator|maintain|doctor) (community build)",
|
|
4
|
+
"version": "0.3.55",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
],
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@lmzhen/dsh-evolution-approval": "^0.3.
|
|
34
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
35
|
-
"@lmzhen/dsh-evolution-maintenance": "^0.3.
|
|
33
|
+
"@lmzhen/dsh-evolution-approval": "^0.3.55",
|
|
34
|
+
"@lmzhen/dsh-evolution-core": "^0.3.55",
|
|
35
|
+
"@lmzhen/dsh-evolution-maintenance": "^0.3.55"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@lmzhen/dsh-evolution-agent-preset": "^0.3.
|
|
38
|
+
"@lmzhen/dsh-evolution-agent-preset": "^0.3.55"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@deepseek-ai/cordis": "^4.0.1",
|