@lmzhen/dsh-evolution-commands 0.3.76 → 0.3.78
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/README.md +6 -0
- package/lib/index.js +214 -25
- package/lib/types/doctor.d.ts +25 -0
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -29,5 +29,11 @@ Independent of request-prefix construction. This package does not alter the asse
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
- No known durable consumer gaps at this time. Runtime contracts are covered by package and boundary tests.
|
|
32
|
+
- **① variant form: the `/evolution …` command face is visible in every session**
|
|
33
|
+
(accepted, not fixed — 0.3.78). Commands are registered in scope layers, and the
|
|
34
|
+
family registers once on the host plane, so a session on a platform original
|
|
35
|
+
preset still lists the management-only subcommands even though no automatic
|
|
36
|
+
family behaviour reaches it. The difference and its reasoning are single-sourced
|
|
37
|
+
in `INSTALL.md` ("Known difference").
|
|
32
38
|
|
|
33
39
|
**Runtime invariant:** No companion is published. The platform auto-assembles nothing and the family mounts no `<pkg>/invariant` cordis row, so a companion here would never execute (v37 S2.1 / I-3).
|
package/lib/index.js
CHANGED
|
@@ -102,6 +102,165 @@ function memoryInterpolationIssues(home) {
|
|
|
102
102
|
}
|
|
103
103
|
return issues;
|
|
104
104
|
}
|
|
105
|
+
/** G3-② (B2): the family delta container. An optionalDependency of this
|
|
106
|
+
* package, so resolution may legitimately fail; every failure degrades to
|
|
107
|
+
* "unknown" — the doctor never throws.
|
|
108
|
+
* Platform anchor: agentPresets.read(id) — preset/agent-presets/src/index.ts:501. */
|
|
109
|
+
const AGENT_PRESET_PACKAGE = "@lmzhen/dsh-evolution-agent-preset";
|
|
110
|
+
/** Resolve one asset the package declares in its exports map.
|
|
111
|
+
* @param asset - bases.json or agent.cordis.yml.
|
|
112
|
+
* @returns the file path, or null when the package is not installed. */
|
|
113
|
+
function resolveAgentPresetAsset(asset) {
|
|
114
|
+
try {
|
|
115
|
+
return fileURLToPath(import.meta.resolve(`${AGENT_PRESET_PACKAGE}/${asset}`));
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** The base rows of bases.json: the comparison iterates the same table the
|
|
121
|
+
* installer reads, so doctor cannot check a different set of variants.
|
|
122
|
+
* @param path - resolved bases.json.
|
|
123
|
+
* @returns the rows, or null when the table is unreadable or malformed. */
|
|
124
|
+
function readAgentPresetBases$1(path) {
|
|
125
|
+
try {
|
|
126
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
127
|
+
if (!Array.isArray(parsed.bases)) return null;
|
|
128
|
+
const rows = [];
|
|
129
|
+
for (const raw of parsed.bases) {
|
|
130
|
+
const entry = raw;
|
|
131
|
+
if (typeof entry.name !== "string" || typeof entry.id !== "string") return null;
|
|
132
|
+
rows.push({
|
|
133
|
+
name: entry.name,
|
|
134
|
+
id: entry.id
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return rows.length > 0 ? rows : null;
|
|
138
|
+
} catch {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Preset directories already on disk, used to keep an installed variant
|
|
143
|
+
* VISIBLE when the base table itself could not be read.
|
|
144
|
+
* @param root - the .agent-presets directory.
|
|
145
|
+
* @returns the directory names; empty when nothing is installed. */
|
|
146
|
+
function installedPresetIds(root) {
|
|
147
|
+
const ids = [];
|
|
148
|
+
let entries;
|
|
149
|
+
try {
|
|
150
|
+
entries = readdirSync(root, { withFileTypes: true });
|
|
151
|
+
} catch {
|
|
152
|
+
return ids;
|
|
153
|
+
}
|
|
154
|
+
for (const entry of entries) if (entry.isDirectory()) ids.push(entry.name);
|
|
155
|
+
return ids;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* G3-② (B2): recompute what a fresh generation would write for every
|
|
159
|
+
* installed variant and compare it byte-for-byte with the file on disk.
|
|
160
|
+
*
|
|
161
|
+
* The recomputation goes through composePresetComposition — the SAME rule
|
|
162
|
+
* /evolution preset install and install-layered.mjs apply (collision guard +
|
|
163
|
+
* row-overrides.json), so doctor cannot disagree with the installer about what
|
|
164
|
+
* "fresh" means. Read-only by contract: the snapshot is never rewritten.
|
|
165
|
+
* @param home - resolved DSH_HOME.
|
|
166
|
+
* @param registry - the platform agent-preset registry, undefined when unmounted.
|
|
167
|
+
* @returns one row per base in the family bases.json.
|
|
168
|
+
*/
|
|
169
|
+
async function probePresetFreshness(home, registry) {
|
|
170
|
+
const presetsRoot = join(home, ".agent-presets");
|
|
171
|
+
const basesPath = resolveAgentPresetAsset("bases.json");
|
|
172
|
+
const deltaPath = resolveAgentPresetAsset("agent.cordis.yml");
|
|
173
|
+
const bases = basesPath === null ? null : readAgentPresetBases$1(basesPath);
|
|
174
|
+
let delta = null;
|
|
175
|
+
let deltaFailure = `${AGENT_PRESET_PACKAGE}/agent.cordis.yml could not be resolved — is the family agent-preset package installed?`;
|
|
176
|
+
if (deltaPath !== null) try {
|
|
177
|
+
delta = readFileSync(deltaPath, "utf8");
|
|
178
|
+
} catch (error) {
|
|
179
|
+
deltaFailure = `${deltaPath} could not be read (${error instanceof Error ? error.message : String(error)})`;
|
|
180
|
+
}
|
|
181
|
+
if (bases === null) {
|
|
182
|
+
const tablePath = basesPath ?? `${AGENT_PRESET_PACKAGE}/bases.json`;
|
|
183
|
+
return installedPresetIds(presetsRoot).map((id) => ({
|
|
184
|
+
base: id,
|
|
185
|
+
destination: join(presetsRoot, id),
|
|
186
|
+
status: "unknown",
|
|
187
|
+
detail: `the family base table could not be read (${tablePath}) — a fresh generation cannot be recomputed`
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
const rows = [];
|
|
191
|
+
for (const base of bases) {
|
|
192
|
+
const destination = join(presetsRoot, base.id);
|
|
193
|
+
const compositionPath = join(destination, "agent.cordis.yml");
|
|
194
|
+
if (!existsSync(compositionPath)) {
|
|
195
|
+
rows.push({
|
|
196
|
+
base: base.name,
|
|
197
|
+
destination,
|
|
198
|
+
status: "absent"
|
|
199
|
+
});
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (registry === void 0) {
|
|
203
|
+
rows.push({
|
|
204
|
+
base: base.name,
|
|
205
|
+
destination,
|
|
206
|
+
status: "unknown",
|
|
207
|
+
detail: "the platform agent-preset registry is not mounted, so the runtime composition cannot be read"
|
|
208
|
+
});
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (delta === null) {
|
|
212
|
+
rows.push({
|
|
213
|
+
base: base.name,
|
|
214
|
+
destination,
|
|
215
|
+
status: "unknown",
|
|
216
|
+
detail: deltaFailure
|
|
217
|
+
});
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
let platform;
|
|
221
|
+
try {
|
|
222
|
+
platform = await registry.read(base.name);
|
|
223
|
+
} catch (error) {
|
|
224
|
+
rows.push({
|
|
225
|
+
base: base.name,
|
|
226
|
+
destination,
|
|
227
|
+
status: "unknown",
|
|
228
|
+
detail: `the runtime composition for base "${base.name}" could not be read (${error instanceof Error ? error.message : String(error)})`
|
|
229
|
+
});
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
let fresh;
|
|
233
|
+
try {
|
|
234
|
+
fresh = composePresetComposition(platform, delta);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
rows.push({
|
|
237
|
+
base: base.name,
|
|
238
|
+
destination,
|
|
239
|
+
status: "unknown",
|
|
240
|
+
detail: `a fresh generation refused to compose (${error instanceof Error ? error.message : String(error)})`
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
let onDisk;
|
|
245
|
+
try {
|
|
246
|
+
onDisk = readFileSync(compositionPath, "utf8");
|
|
247
|
+
} catch (error) {
|
|
248
|
+
rows.push({
|
|
249
|
+
base: base.name,
|
|
250
|
+
destination,
|
|
251
|
+
status: "unknown",
|
|
252
|
+
detail: `${compositionPath} could not be read (${error instanceof Error ? error.message : String(error)})`
|
|
253
|
+
});
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
rows.push({
|
|
257
|
+
base: base.name,
|
|
258
|
+
destination,
|
|
259
|
+
status: fresh === onDisk ? "fresh" : "differs"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
return rows;
|
|
263
|
+
}
|
|
105
264
|
/**
|
|
106
265
|
* Diagnose the deployment. `ctx` supplies service presence (a bare stub with
|
|
107
266
|
* only `get` is enough); `home` defaults to the evolution root so tests can
|
|
@@ -115,6 +274,7 @@ async function diagnose(ctx, options = {}) {
|
|
|
115
274
|
conflicts.push(scope === "profiles-dir" ? `the home profiles directory could not be enumerated (${detail}) — bundle-based install-form and conflict checks are DEGRADED and may miss installed bundles` : `a profile manifest could not be read or parsed (${detail}) — this profile's bundle rows are UNKNOWN, so the install-form and conflict checks are DEGRADED and may miss installed bundles`);
|
|
116
275
|
});
|
|
117
276
|
const has = (name) => ctx.get(name) !== void 0;
|
|
277
|
+
const presetFreshness = await probePresetFreshness(home, ctx.get("agentPresets"));
|
|
118
278
|
const full = bundles.some((name) => tailOf(name) === "dsh-evolution-all");
|
|
119
279
|
const host = bundles.some((name) => tailOf(name) === "dsh-evolution-host");
|
|
120
280
|
const preset = bundles.some((name) => tailOf(name) === "dsh-evolution-preset");
|
|
@@ -145,6 +305,7 @@ async function diagnose(ctx, options = {}) {
|
|
|
145
305
|
executingCount = null;
|
|
146
306
|
}
|
|
147
307
|
const installForm = full ? "full" : preset ? "preset" : layered ? "layered" : host ? "host" : presetDirInstalled ? "preset-only" : "none";
|
|
308
|
+
const deploymentForm = installForm === "layered" ? "variant" : installForm === "full" || installForm === "preset" ? "attach" : installForm === "host" ? "host-only" : installForm === "preset-only" ? "preset-only" : "none";
|
|
148
309
|
const mountConflicts = conflicts.filter((row) => !row.includes("DEGRADED"));
|
|
149
310
|
const undecidable = mountConflicts.length !== conflicts.length;
|
|
150
311
|
const actions = [];
|
|
@@ -152,7 +313,7 @@ async function diagnose(ctx, options = {}) {
|
|
|
152
313
|
else if (undecidable) actions.push("The install form is UNDECIDABLE (see the DEGRADED row above): an unreadable profiles directory or profile manifest hides whatever is installed, so this report must not add or remove a bundle. Fix the reported read failure, then re-run /evolution doctor.");
|
|
153
314
|
else if (installForm === "none") actions.push("Install the default full bundle: dsh plugin --profile web add @lmzhen/dsh-evolution-all");
|
|
154
315
|
else if (installForm === "preset-only") actions.push("The Evolution preset is delivered but no profile mounts an evolution bundle — re-add @lmzhen/dsh-evolution-host for the layered layout (do NOT add all on top of the preset: that double-mounts the model rows), or remove .agent-presets/evolution if the layered layout is no longer wanted.");
|
|
155
|
-
else if (installForm === "layered") actions.push("
|
|
316
|
+
else if (installForm === "layered") actions.push("Variant form (session opt-in): model tools follow the Evolution preset, and a session on a platform original preset carries no family rows; add @lmzhen/dsh-evolution-all instead if every session should have them.");
|
|
156
317
|
const env = envIssues();
|
|
157
318
|
if (env.length > 0) actions.push("Fix the DSH_EVOLUTION_* variable listed above.");
|
|
158
319
|
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.");
|
|
@@ -160,8 +321,13 @@ async function diagnose(ctx, options = {}) {
|
|
|
160
321
|
const memoryIssues = memoryInterpolationIssues(home);
|
|
161
322
|
if (memoryIssues.length > 0) actions.push("Rewrite the memory entries listed above (or run a build with the render-time neutralization) — they broke prompt assembly on older builds.");
|
|
162
323
|
if ((executingCount ?? 0) > 0) actions.push(`${executingCount} staged write(s) are EXECUTING (an approve crashed mid-run — or one is still in flight). Inspect with /evolution pending: if you started the approve, verify the landed write and do not reject it; only reject after verifying no write is intended.`);
|
|
324
|
+
for (const row of presetFreshness) {
|
|
325
|
+
if (row.status !== "differs") continue;
|
|
326
|
+
actions.push(`Preset variant ${row.destination} DIFFERS from a fresh generation — it is an install-time snapshot; re-run the installer (/evolution preset install) to regenerate`);
|
|
327
|
+
}
|
|
163
328
|
return {
|
|
164
329
|
installForm,
|
|
330
|
+
deploymentForm,
|
|
165
331
|
bundles,
|
|
166
332
|
conflicts,
|
|
167
333
|
envIssues: env,
|
|
@@ -169,17 +335,23 @@ async function diagnose(ctx, options = {}) {
|
|
|
169
335
|
services,
|
|
170
336
|
pendingCount,
|
|
171
337
|
executingCount,
|
|
338
|
+
presetFreshness,
|
|
172
339
|
actions
|
|
173
340
|
};
|
|
174
341
|
}
|
|
175
342
|
function renderDoctorText(report) {
|
|
176
343
|
const lines = [
|
|
177
|
-
`Evolution doctor — install form: ${report.installForm}`,
|
|
344
|
+
`Evolution doctor — install form: ${report.installForm} (deployment: ${report.deploymentForm})`,
|
|
178
345
|
`bundles (all profiles): ${report.bundles.length > 0 ? report.bundles.join(", ") : "(none)"}`,
|
|
179
346
|
`services: review=${report.services.review} (inferred from bundles, all profiles) curator=${report.services.curator} approval=${report.services.approval} skillUsage=${report.services.skillUsage} io=${report.services.io}`,
|
|
180
347
|
`pending: ${report.pendingCount === null ? "unknown" : report.pendingCount}`,
|
|
181
348
|
`executing: ${report.executingCount === null ? "unknown" : report.executingCount}`
|
|
182
349
|
];
|
|
350
|
+
for (const row of report.presetFreshness) {
|
|
351
|
+
if (row.status === "absent") continue;
|
|
352
|
+
const verdict = row.status === "fresh" ? "fresh — a fresh generation matches this file byte-for-byte" : row.status === "differs" ? "DIFFERS from a fresh generation — it is an install-time snapshot; re-run the installer (/evolution preset install) to regenerate" : `unknown — a fresh generation could not be recomputed (${row.detail ?? "reason not recorded"})`;
|
|
353
|
+
lines.push(`preset: ${row.destination} ${verdict}`);
|
|
354
|
+
}
|
|
183
355
|
if (report.conflicts.length > 0) lines.push("conflicts:", ...report.conflicts.map((line) => ` ! ${line}`));
|
|
184
356
|
if (report.envIssues.length > 0) lines.push("env:", ...report.envIssues.map((line) => ` ! ${line}`));
|
|
185
357
|
if (report.memoryIssues.length > 0) lines.push("memory:", ...report.memoryIssues.map((line) => ` ! ${line}`));
|
|
@@ -242,8 +414,8 @@ const COMMAND_ENTRIES = [
|
|
|
242
414
|
summary: "run a maintenance scan (--facts: 0-token preview)"
|
|
243
415
|
},
|
|
244
416
|
{
|
|
245
|
-
usage: "preset install [--base <name>]",
|
|
246
|
-
summary: "generate
|
|
417
|
+
usage: "preset install [--base <name>[,<name>...]]",
|
|
418
|
+
summary: "generate one Evolution agent preset per named base into the user root (bases from the agent package's bases.json)"
|
|
247
419
|
},
|
|
248
420
|
{
|
|
249
421
|
usage: "restructure <name> \"<heading>\" <to_file> [--plan <runId>]",
|
|
@@ -555,7 +727,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
555
727
|
if (/^maintain\b/.test(input)) return err("Unknown maintain arguments: expected bare `maintain`, `maintain --timeout <ms>` or `maintain --timeout=<ms>` (a positive integer). Got: " + input);
|
|
556
728
|
const presetInstall = /^preset install(?: --base (\S+))?$/.exec(input);
|
|
557
729
|
if (presetInstall !== null || /^preset\b/.test(input)) {
|
|
558
|
-
if (presetInstall === null) return err("Unknown preset arguments: expected `preset install` or `preset install --base <name
|
|
730
|
+
if (presetInstall === null) return err("Unknown preset arguments: expected `preset install` or `preset install --base <name>[,<name>...]` (the names live in the agent package's bases.json). Got: " + input);
|
|
559
731
|
{
|
|
560
732
|
let installedBundles;
|
|
561
733
|
try {
|
|
@@ -572,27 +744,43 @@ function apply(ctx, rawConfig = {}) {
|
|
|
572
744
|
try {
|
|
573
745
|
const source = resolveAgentPresetDir(import.meta.url);
|
|
574
746
|
const table = readAgentPresetBases(source);
|
|
575
|
-
const
|
|
576
|
-
const
|
|
577
|
-
if (
|
|
578
|
-
const
|
|
747
|
+
const requestedRaw = presetInstall[1] ?? table.defaultBase;
|
|
748
|
+
const requested = [...new Set(requestedRaw.split(",").map((part) => part.trim()).filter((part) => part !== ""))];
|
|
749
|
+
if (requested.length === 0) return err(`No agent-preset base named — ${join(source, "bases.json")} lists ${table.entries.map((entry) => entry.name).join(", ")}`);
|
|
750
|
+
const selected = [];
|
|
751
|
+
for (const name of requested) {
|
|
752
|
+
const entry = table.entries.find((candidate) => candidate.name === name);
|
|
753
|
+
if (entry === void 0) return err(`Unknown agent-preset base "${name}" — ${join(source, "bases.json")} lists ${table.entries.map((candidate) => candidate.name).join(", ")}`);
|
|
754
|
+
selected.push(entry);
|
|
755
|
+
}
|
|
756
|
+
const rawBases = JSON.parse(readFileSync(join(source, "bases.json"), "utf8"));
|
|
579
757
|
const deltaPath = join(source, "agent.cordis.yml");
|
|
580
|
-
|
|
581
|
-
if (!existsSync(deltaPath) || !existsSync(presetPath)) return err(`Preset file missing from ${source} — is the dsh-evolution-agent-preset package installed?`);
|
|
758
|
+
if (!existsSync(deltaPath)) return err(`Preset file missing from ${source} — is the dsh-evolution-agent-preset package installed?`);
|
|
582
759
|
const registry = ctx.get("agentPresets");
|
|
583
760
|
if (!registry) return err("Agent preset registry not mounted — cannot compose the Evolution preset against the runtime standard.");
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
name:
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
761
|
+
const written = [];
|
|
762
|
+
for (const base of selected) {
|
|
763
|
+
const ability = rawBases.bases?.find((candidate) => candidate.name === base.name);
|
|
764
|
+
if (typeof ability?.unsupported === "string") return err(`Base "${base.name}" is registered as UNSUPPORTED: ${ability.unsupported}`);
|
|
765
|
+
const requiredService = ability?.requires?.service;
|
|
766
|
+
if (typeof requiredService === "string" && ctx.get(requiredService) === void 0) return err(`Base "${base.name}" requires the "${requiredService}" service, which this deployment does not provide — the generated preset would refuse to mount.`);
|
|
767
|
+
const target = join(evolutionRoot(), ".agent-presets", base.id);
|
|
768
|
+
const presetPath = join(source, base.metadata);
|
|
769
|
+
if (!existsSync(presetPath)) return err(`Preset file missing from ${source} — is the dsh-evolution-agent-preset package installed?`);
|
|
770
|
+
const composition = composePresetComposition(await registry.read(base.name), readFileSync(deltaPath, "utf8"));
|
|
771
|
+
mkdirSync(target, { recursive: true });
|
|
772
|
+
atomicWriteFiles(target, [{
|
|
773
|
+
name: "agent.cordis.yml",
|
|
774
|
+
content: composition
|
|
775
|
+
}, {
|
|
776
|
+
name: base.metadata,
|
|
777
|
+
content: readFileSync(presetPath)
|
|
778
|
+
}], void 0, (message) => {
|
|
779
|
+
ctx.logger.warn(message);
|
|
780
|
+
});
|
|
781
|
+
written.push(`${base.name} → ${target}`);
|
|
782
|
+
}
|
|
783
|
+
return ok(`Evolution agent preset installed (${written.join("; ")}). Restart the session switcher to select it.`);
|
|
596
784
|
} catch (error) {
|
|
597
785
|
return err(`Preset install failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
598
786
|
}
|
|
@@ -727,8 +915,9 @@ const defaultFs = {
|
|
|
727
915
|
mtime: (path) => {
|
|
728
916
|
try {
|
|
729
917
|
return statSync(path).mtimeMs;
|
|
730
|
-
} catch {
|
|
731
|
-
return null;
|
|
918
|
+
} catch (error) {
|
|
919
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") return null;
|
|
920
|
+
throw error;
|
|
732
921
|
}
|
|
733
922
|
}
|
|
734
923
|
};
|
package/lib/types/doctor.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/** G3-② (B2): one delivered preset variant compared against a fresh generation.
|
|
2
|
+
* "absent" is the user simply not using that base (never an action); "unknown"
|
|
3
|
+
* is a comparison that could not run, kept distinct from a verified "fresh". */
|
|
4
|
+
export interface PresetFreshnessRow {
|
|
5
|
+
/** Agent-preset base name (the id the platform registry read() takes). */
|
|
6
|
+
base: string;
|
|
7
|
+
/** Install destination <home>/.agent-presets/<base.id> that was compared. */
|
|
8
|
+
destination: string;
|
|
9
|
+
status: 'fresh' | 'differs' | 'absent' | 'unknown';
|
|
10
|
+
/** Why the comparison could not run — set on unknown rows only. */
|
|
11
|
+
detail?: string;
|
|
12
|
+
}
|
|
1
13
|
export interface DoctorReport {
|
|
2
14
|
/** OPT-23 (2026-09): `preset-only` — the delivered Evolution preset
|
|
3
15
|
* artifact exists but NO profile carries an evolution bundle (e.g. the
|
|
@@ -6,6 +18,13 @@ export interface DoctorReport {
|
|
|
6
18
|
* installing `all` — which double-mounts the preset's model rows (the
|
|
7
19
|
* exact conflict this report flags). */
|
|
8
20
|
installForm: 'full' | 'host' | 'preset' | 'layered' | 'preset-only' | 'none';
|
|
21
|
+
/** The PRODUCT form the install form implies (0.3.77, C axis). `variant` =
|
|
22
|
+
* session-level opt-in: the model tools live in the Evolution preset, so a
|
|
23
|
+
* session on any platform original preset carries none of the family and the
|
|
24
|
+
* cross-session consumers skip it (config `sessionScoped`). `attach` =
|
|
25
|
+
* profile-level: the model rows sit at profile root and every session carries
|
|
26
|
+
* them. The two are mutually exclusive install targets (E-33). */
|
|
27
|
+
deploymentForm: 'variant' | 'attach' | 'host-only' | 'preset-only' | 'none';
|
|
9
28
|
/** Aggregated across ALL profiles under `home` (N13, v12): doctor answers
|
|
10
29
|
* "is any profile carrying an evolution bundle / which install forms exist"
|
|
11
30
|
* — not "what this runtime mounted". `services.review` is the runtime-side
|
|
@@ -31,6 +50,12 @@ export interface DoctorReport {
|
|
|
31
50
|
/** v23 (AP-2): claimed-but-crashed records — the only state that needs an
|
|
32
51
|
* operator action (reject) to clear; surfaced separately from pending. */
|
|
33
52
|
executingCount: number | null;
|
|
53
|
+
/** G3-② (B2): how the delivered preset variants compare with what a fresh
|
|
54
|
+
* generation would write against THIS runtime platform. A variant embeds the
|
|
55
|
+
* platform composition of its install day, so a platform change or a family
|
|
56
|
+
* upgrade leaves a file describing a platform that no longer exists. Read-only:
|
|
57
|
+
* a stale snapshot is reported, never repaired. */
|
|
58
|
+
presetFreshness: PresetFreshnessRow[];
|
|
34
59
|
actions: string[];
|
|
35
60
|
}
|
|
36
61
|
/** Profile bundle rows for evolution-family packages across all profiles.
|
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.78",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
30
|
-
"@lmzhen/dsh-evolution-approval": "^0.3.
|
|
31
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
32
|
-
"@lmzhen/dsh-evolution-maintenance": "^0.3.
|
|
30
|
+
"@lmzhen/dsh-evolution-approval": "^0.3.78",
|
|
31
|
+
"@lmzhen/dsh-evolution-core": "^0.3.78",
|
|
32
|
+
"@lmzhen/dsh-evolution-maintenance": "^0.3.78"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@lmzhen/dsh-evolution-agent-preset": "^0.3.
|
|
35
|
+
"@lmzhen/dsh-evolution-agent-preset": "^0.3.78"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.1",
|