@lmzhen/dsh-evolution-commands 0.3.76 → 0.3.77

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
@@ -145,6 +145,7 @@ async function diagnose(ctx, options = {}) {
145
145
  executingCount = null;
146
146
  }
147
147
  const installForm = full ? "full" : preset ? "preset" : layered ? "layered" : host ? "host" : presetDirInstalled ? "preset-only" : "none";
148
+ const deploymentForm = installForm === "layered" ? "variant" : installForm === "full" || installForm === "preset" ? "attach" : installForm === "host" ? "host-only" : installForm === "preset-only" ? "preset-only" : "none";
148
149
  const mountConflicts = conflicts.filter((row) => !row.includes("DEGRADED"));
149
150
  const undecidable = mountConflicts.length !== conflicts.length;
150
151
  const actions = [];
@@ -152,7 +153,7 @@ async function diagnose(ctx, options = {}) {
152
153
  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
154
  else if (installForm === "none") actions.push("Install the default full bundle: dsh plugin --profile web add @lmzhen/dsh-evolution-all");
154
155
  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("Model tools follow the Evolution preset per session; add @lmzhen/dsh-evolution-all instead if every session should have them.");
156
+ 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
157
  const env = envIssues();
157
158
  if (env.length > 0) actions.push("Fix the DSH_EVOLUTION_* variable listed above.");
158
159
  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.");
@@ -162,6 +163,7 @@ async function diagnose(ctx, options = {}) {
162
163
  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.`);
163
164
  return {
164
165
  installForm,
166
+ deploymentForm,
165
167
  bundles,
166
168
  conflicts,
167
169
  envIssues: env,
@@ -174,7 +176,7 @@ async function diagnose(ctx, options = {}) {
174
176
  }
175
177
  function renderDoctorText(report) {
176
178
  const lines = [
177
- `Evolution doctor — install form: ${report.installForm}`,
179
+ `Evolution doctor — install form: ${report.installForm} (deployment: ${report.deploymentForm})`,
178
180
  `bundles (all profiles): ${report.bundles.length > 0 ? report.bundles.join(", ") : "(none)"}`,
179
181
  `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
182
  `pending: ${report.pendingCount === null ? "unknown" : report.pendingCount}`,
@@ -242,8 +244,8 @@ const COMMAND_ENTRIES = [
242
244
  summary: "run a maintenance scan (--facts: 0-token preview)"
243
245
  },
244
246
  {
245
- usage: "preset install [--base <name>]",
246
- summary: "generate the Evolution agent preset into the user root (bases from the agent package's bases.json)"
247
+ usage: "preset install [--base <name>[,<name>...]]",
248
+ summary: "generate one Evolution agent preset per named base into the user root (bases from the agent package's bases.json)"
247
249
  },
248
250
  {
249
251
  usage: "restructure <name> \"<heading>\" <to_file> [--plan <runId>]",
@@ -555,7 +557,7 @@ function apply(ctx, rawConfig = {}) {
555
557
  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
558
  const presetInstall = /^preset install(?: --base (\S+))?$/.exec(input);
557
559
  if (presetInstall !== null || /^preset\b/.test(input)) {
558
- if (presetInstall === null) return err("Unknown preset arguments: expected `preset install` or `preset install --base <name>` (the names live in the agent package's bases.json). Got: " + input);
560
+ 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
561
  {
560
562
  let installedBundles;
561
563
  try {
@@ -572,27 +574,38 @@ function apply(ctx, rawConfig = {}) {
572
574
  try {
573
575
  const source = resolveAgentPresetDir(import.meta.url);
574
576
  const table = readAgentPresetBases(source);
575
- const requested = presetInstall[1] ?? table.defaultBase;
576
- const base = table.entries.find((entry) => entry.name === requested);
577
- if (base === void 0) return err(`Unknown agent-preset base "${requested}" — ${join(source, "bases.json")} lists ${table.entries.map((entry) => entry.name).join(", ")}`);
578
- const target = join(evolutionRoot(), ".agent-presets", base.id);
577
+ const requestedRaw = presetInstall[1] ?? table.defaultBase;
578
+ const requested = [...new Set(requestedRaw.split(",").map((part) => part.trim()).filter((part) => part !== ""))];
579
+ if (requested.length === 0) return err(`No agent-preset base named — ${join(source, "bases.json")} lists ${table.entries.map((entry) => entry.name).join(", ")}`);
580
+ const selected = [];
581
+ for (const name of requested) {
582
+ const entry = table.entries.find((candidate) => candidate.name === name);
583
+ if (entry === void 0) return err(`Unknown agent-preset base "${name}" — ${join(source, "bases.json")} lists ${table.entries.map((candidate) => candidate.name).join(", ")}`);
584
+ selected.push(entry);
585
+ }
579
586
  const deltaPath = join(source, "agent.cordis.yml");
580
- const presetPath = join(source, base.metadata);
581
- if (!existsSync(deltaPath) || !existsSync(presetPath)) return err(`Preset file missing from ${source} — is the dsh-evolution-agent-preset package installed?`);
587
+ if (!existsSync(deltaPath)) return err(`Preset file missing from ${source} — is the dsh-evolution-agent-preset package installed?`);
582
588
  const registry = ctx.get("agentPresets");
583
589
  if (!registry) return err("Agent preset registry not mounted — cannot compose the Evolution preset against the runtime standard.");
584
- const composition = composePresetComposition(await registry.read(base.name), readFileSync(deltaPath, "utf8"));
585
- mkdirSync(target, { recursive: true });
586
- atomicWriteFiles(target, [{
587
- name: "agent.cordis.yml",
588
- content: composition
589
- }, {
590
- name: base.metadata,
591
- content: readFileSync(presetPath)
592
- }], void 0, (message) => {
593
- ctx.logger.warn(message);
594
- });
595
- return ok(`Evolution agent preset installed to ${target} (runtime ${base.name} + delta). Restart the session switcher to select it.`);
590
+ const written = [];
591
+ for (const base of selected) {
592
+ const target = join(evolutionRoot(), ".agent-presets", base.id);
593
+ const presetPath = join(source, base.metadata);
594
+ if (!existsSync(presetPath)) return err(`Preset file missing from ${source} — is the dsh-evolution-agent-preset package installed?`);
595
+ const composition = composePresetComposition(await registry.read(base.name), readFileSync(deltaPath, "utf8"));
596
+ mkdirSync(target, { recursive: true });
597
+ atomicWriteFiles(target, [{
598
+ name: "agent.cordis.yml",
599
+ content: composition
600
+ }, {
601
+ name: base.metadata,
602
+ content: readFileSync(presetPath)
603
+ }], void 0, (message) => {
604
+ ctx.logger.warn(message);
605
+ });
606
+ written.push(`${base.name} → ${target}`);
607
+ }
608
+ return ok(`Evolution agent preset installed (${written.join("; ")}). Restart the session switcher to select it.`);
596
609
  } catch (error) {
597
610
  return err(`Preset install failed: ${error instanceof Error ? error.message : String(error)}`);
598
611
  }
@@ -6,6 +6,13 @@ export interface DoctorReport {
6
6
  * installing `all` — which double-mounts the preset's model rows (the
7
7
  * exact conflict this report flags). */
8
8
  installForm: 'full' | 'host' | 'preset' | 'layered' | 'preset-only' | 'none';
9
+ /** The PRODUCT form the install form implies (0.3.77, C axis). `variant` =
10
+ * session-level opt-in: the model tools live in the Evolution preset, so a
11
+ * session on any platform original preset carries none of the family and the
12
+ * cross-session consumers skip it (config `sessionScoped`). `attach` =
13
+ * profile-level: the model rows sit at profile root and every session carries
14
+ * them. The two are mutually exclusive install targets (E-33). */
15
+ deploymentForm: 'variant' | 'attach' | 'host-only' | 'preset-only' | 'none';
9
16
  /** Aggregated across ALL profiles under `home` (N13, v12): doctor answers
10
17
  * "is any profile carrying an evolution bundle / which install forms exist"
11
18
  * — not "what this runtime mounted". `services.review` is the runtime-side
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.76",
4
+ "version": "0.3.77",
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.76",
31
- "@lmzhen/dsh-evolution-core": "^0.3.76",
32
- "@lmzhen/dsh-evolution-maintenance": "^0.3.76"
30
+ "@lmzhen/dsh-evolution-approval": "^0.3.77",
31
+ "@lmzhen/dsh-evolution-core": "^0.3.77",
32
+ "@lmzhen/dsh-evolution-maintenance": "^0.3.77"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@lmzhen/dsh-evolution-agent-preset": "^0.3.76"
35
+ "@lmzhen/dsh-evolution-agent-preset": "^0.3.77"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.1",