@lmzhen/dsh-evolution-commands 0.6.1 → 0.8.0

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.
Files changed (2) hide show
  1. package/lib/index.js +49 -33
  2. package/package.json +5 -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 { DEFAULT_SKILL_LIMITS, MAX_TIMER_DELAY_MS, PARAM_EXPOSURE, PARAM_NAMESPACES, appendEvolutionEvent, assertSkillsRootAliasRetired, buildLearnPrompt, canonicalWriteId, clampedNumber, composePresetComposition, eventsFile, evolutionRoot, isDeprecatedParamId, isMissingPath, newSkillLibrary, policyStageLimits, resolveParamId, resolveRootConfig, scopedProbeReport } from "@lmzhen/dsh-evolution-core";
5
+ import { DEFAULT_SKILL_LIMITS, MAX_TIMER_DELAY_MS, PARAM_EXPOSURE, PARAM_NAMESPACES, appendEvolutionEvent, assertSkillsRootAliasRetired, buildLearnPrompt, canonicalWriteId, clampedNumber, composePresetComposition, errorText, eventsFile, evolutionRoot, isDeprecatedParamId, isMissingPath, newSkillLibrary, policyStageLimits, resolveParamId, resolveRootConfig, scopedProbeReport } 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";
@@ -833,18 +833,18 @@ function apply(ctx, rawConfig = {}) {
833
833
  text
834
834
  });
835
835
  const invocationAgent = invocation.agent;
836
- const agentMissing = (need) => invocationAgent === void 0 ? err(`E-305: this invocation carries no agent \`${need}\` needs a session-backed call (run it from a session in the GUI or the CLI).`) : void 0;
836
+ const agentMissing = (need) => invocationAgent === void 0 ? err(errorText("e-305-this-invocation-carries-no", { a1: need })) : void 0;
837
837
  const unreplayableWriteRefusal = (what) => {
838
838
  if (approval === void 0) return void 0;
839
839
  const session = invocationAgent?.session;
840
840
  const sessionPolicy = effectiveSessionPolicy(ctx, session);
841
841
  if (!(approval.isEnabled !== false && sessionPolicy !== "never" && approval.stageForeground !== false)) return void 0;
842
- return err(`E-306: this deployment stages foreground writes, but \`/evolution ${what}\` is not replayable through the skill runner — there is nothing to stage. Run it from a session whose approval policy is 'never', or set \`stageForeground: false\` on the evolution-approval row, then repeat the command.`);
842
+ return err(errorText("e-306-this-deployment-stages-foreground", { a1: what }));
843
843
  };
844
844
  const approval = ctx.get("evolutionApproval");
845
845
  const pendingMatch = /^pending(?: --detail)?$/.exec(input);
846
846
  if (pendingMatch) {
847
- if (!approval) return err("E-301: approval service not mounted — pending writes cannot be listed or replayed. Next: the evolution-approval row ships with evolution-host/evolution-all — run /evolution doctor to see which services are mounted.");
847
+ if (!approval) return err(errorText("e-301-approval-service-not-mounted"));
848
848
  const listed = [...await approval.list("pending"), ...await approval.list("executing")];
849
849
  const pending = [...new Map(listed.map((row) => [row.id, row])).values()];
850
850
  if (pending.length === 0) return ok("No pending evolution writes.");
@@ -860,7 +860,7 @@ function apply(ctx, rawConfig = {}) {
860
860
  const id = input.slice(8).trim();
861
861
  const result = approval ? await approval.approve(id) : {
862
862
  ok: false,
863
- message: "E-301: approval service not mounted. Next: the evolution-approval row ships with evolution-host/evolution-all — run /evolution doctor to see which services are mounted."
863
+ message: errorText("e-301-approval-service-not-mounted-2")
864
864
  };
865
865
  return result.ok ? ok(result.message) : err(result.message);
866
866
  }
@@ -868,27 +868,27 @@ function apply(ctx, rawConfig = {}) {
868
868
  const id = input.slice(7).trim();
869
869
  const result = approval ? await approval.reject(id) : {
870
870
  ok: false,
871
- message: "E-301: approval service not mounted. Next: the evolution-approval row ships with evolution-host/evolution-all — run /evolution doctor to see which services are mounted."
871
+ message: errorText("e-301-approval-service-not-mounted-2")
872
872
  };
873
873
  return result.ok ? ok(result.message) : err(result.message);
874
874
  }
875
875
  if (input.startsWith("release ")) {
876
876
  const id = input.slice(8).trim();
877
- if (!approval) return err("E-301: approval service not mounted. Next: the evolution-approval row ships with evolution-host/evolution-all — run /evolution doctor to see which services are mounted.");
878
- if (!approval.release) return err("E-304: the mounted approval service predates the release capability — reject the record instead.");
877
+ if (!approval) return err(errorText("e-301-approval-service-not-mounted-2"));
878
+ if (!approval.release) return err(errorText("e-304-the-mounted-approval-service"));
879
879
  const result = await approval.release(id);
880
880
  return result.ok ? ok(result.message) : err(result.message);
881
881
  }
882
882
  if (input === "curator run") {
883
883
  const curator = ctx.get("evolutionCurator");
884
- if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
884
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
885
885
  const result = await curator.run({ ignoreGates: true });
886
886
  if (result.skipped !== void 0) return ok(`Curator run skipped (${result.skipped}): no curation pass was executed.\nrunId=${result.report.runId}`);
887
887
  return ok(`Curator run complete: ${result.stale.length} stale, ${result.archived.length} archived, ${result.errors.length} failed.\nrunId=${result.report.runId}${result.report.snapshotPath ? `\nsnapshot=${result.report.snapshotPath}` : ""}`);
888
888
  }
889
889
  if (input === "curator pause" || input === "curator resume") {
890
890
  const curator = ctx.get("evolutionCurator");
891
- if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
891
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
892
892
  const paused = input === "curator pause";
893
893
  await curator.setPaused(paused);
894
894
  const notPersisted = ctx.get("evolutionState") !== void 0 ? "" : "\nNOTE: no evolution state service is mounted — this setting is NOT persisted and will not survive this process.";
@@ -896,7 +896,7 @@ function apply(ctx, rawConfig = {}) {
896
896
  }
897
897
  if (input === "curator status") {
898
898
  const curator = ctx.get("evolutionCurator");
899
- if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
899
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
900
900
  const state = await curator.status();
901
901
  if (!state) return ok("No curator state yet: the first automatic pass is deferred until the interval elapses.");
902
902
  const lastRun = typeof state.lastRunAt === "number" && Number.isFinite(state.lastRunAt) && state.lastRunAt > 0 ? new Date(state.lastRunAt).toISOString() : "unknown";
@@ -909,7 +909,7 @@ function apply(ctx, rawConfig = {}) {
909
909
  }
910
910
  if (input === "mutations") {
911
911
  const curator = ctx.get("evolutionCurator");
912
- if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
912
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
913
913
  const records = await curator.skills.listMutations();
914
914
  const usable = Array.isArray(records) ? records.filter((row) => typeof row === "object" && row !== null && typeof row.at === "string" && typeof row.skillName === "string" && typeof row.action === "string" && typeof row.summary === "string") : [];
915
915
  if (usable.length === 0) return ok("No mutation records yet.");
@@ -918,7 +918,7 @@ function apply(ctx, rawConfig = {}) {
918
918
  }
919
919
  if (input === "curator scope") {
920
920
  const curator = ctx.get("evolutionCurator");
921
- if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
921
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
922
922
  const view = await curator.scopeView();
923
923
  const line = (label, names) => `${label}: ${names.length}${names.length === 0 ? "" : `\n ${names.join(", ")}`}`;
924
924
  return ok([
@@ -932,7 +932,7 @@ function apply(ctx, rawConfig = {}) {
932
932
  }
933
933
  if (input === "curator report") {
934
934
  const curator = ctx.get("evolutionCurator");
935
- if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
935
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
936
936
  const report = await curator.latestReport();
937
937
  if (!report) return ok("No curator report available.");
938
938
  if (typeof report !== "object" || typeof report.runId !== "string" || typeof report.startedAt !== "string" || !Array.isArray(report.archived) || !Array.isArray(report.failed)) return err("Report file unreadable.");
@@ -958,7 +958,7 @@ function apply(ctx, rawConfig = {}) {
958
958
  const curator = ctx.get("evolutionCurator");
959
959
  const result = curator ? await curator.restoreSnapshot() : {
960
960
  ok: false,
961
- message: "E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor."
961
+ message: errorText("e-302-curator-service-not-mounted")
962
962
  };
963
963
  return result.ok ? ok(result.message) : err(result.message);
964
964
  }
@@ -972,7 +972,7 @@ function apply(ctx, rawConfig = {}) {
972
972
  const curator = ctx.get("evolutionCurator");
973
973
  const result = curator ? await curator.consolidate(target, sources) : {
974
974
  ok: false,
975
- message: "E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor."
975
+ message: errorText("e-302-curator-service-not-mounted")
976
976
  };
977
977
  if (!result.ok) return err(result.message);
978
978
  return ok(planRunId ? `${result.message}\n[audit] plan=${planRunId}` : result.message);
@@ -985,13 +985,13 @@ function apply(ctx, rawConfig = {}) {
985
985
  const curator = ctx.get("evolutionCurator");
986
986
  const result = curator ? await curator.restore(name) : {
987
987
  ok: false,
988
- message: "E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor."
988
+ message: errorText("e-302-curator-service-not-mounted")
989
989
  };
990
990
  return result.ok ? ok(result.message) : err(result.message);
991
991
  }
992
992
  if (input === "skills health") {
993
993
  const curator = ctx.get("evolutionCurator");
994
- if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
994
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
995
995
  const [rows, observed] = await Promise.all([curator.healthView(), curator.usageObserved()]);
996
996
  const banner = observed ? "" : "Usage observation not yet established — churn (write-ghost) rows are suppressed.";
997
997
  const line = (row) => `${row.verdict.padEnd(18)} ${row.name}${row.reasons.length > 0 ? ` — ${row.reasons.join("; ")}` : ""}`;
@@ -1024,7 +1024,7 @@ function apply(ctx, rawConfig = {}) {
1024
1024
  agent.followup(message);
1025
1025
  woke = true;
1026
1026
  } else if (typeof agent.inject === "function") agent.inject(message);
1027
- else return err("E-305: the invocation agent exposes neither `followup` nor `inject` — this learn request has no delivery channel.");
1027
+ else return err(errorText("e-305-the-invocation-agent-exposes"));
1028
1028
  const eventIo = ctx.get("evolutionIo")?.provider();
1029
1029
  if (eventIo) appendEvolutionEvent(eventIo, eventsFile(evolutionRoot()), {
1030
1030
  type: "learn",
@@ -1203,7 +1203,7 @@ function apply(ctx, rawConfig = {}) {
1203
1203
  const session = invocationAgent?.session;
1204
1204
  const sessionPolicy = effectiveSessionPolicy(ctx, session);
1205
1205
  const stagesForeground = approval.isEnabled !== false && sessionPolicy !== "never" && approval.stageForeground !== false;
1206
- if (stagesForeground && session === void 0) return err("E-306: this deployment stages foreground writes, but this invocation carries no agent session — `/evolution restructure` would stage a record with no session attribution. Run it from a session in the GUI or the CLI, or set `stageForeground: false` on the evolution-approval row, then repeat the command.");
1206
+ if (stagesForeground && session === void 0) return err(errorText("e-306-this-deployment-stages-foreground-2"));
1207
1207
  if (stagesForeground && !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.");
1208
1208
  const decision = await approval.request({
1209
1209
  kind: "skill",
@@ -1248,7 +1248,7 @@ function apply(ctx, rawConfig = {}) {
1248
1248
  }
1249
1249
  if (input === "replay") {
1250
1250
  const replay = ctx.get("evolutionReplay");
1251
- if (!replay) return err("E-303: replay service not mounted. Next: mount the evolution-replay row (evolution-host/evolution-all) and run /evolution doctor.");
1251
+ if (!replay) return err(errorText("e-303-replay-service-not-mounted"));
1252
1252
  return ok(replay.compare().report);
1253
1253
  }
1254
1254
  const paramsMatch = /^params(?: --group ([a-z-]+))?(?: --json)?$/.exec(input);
@@ -1264,52 +1264,68 @@ function apply(ctx, rawConfig = {}) {
1264
1264
  sections.set(descriptor.ns, section);
1265
1265
  }
1266
1266
  } catch (error) {
1267
- return err(`E-307: the settings service could not report its sections (${error instanceof Error ? error.message : String(error)}). /evolution params needs the user layer to tell an override from a deployment value; run /evolution doctor to see which services are mounted.`);
1267
+ return err(errorText("e-307-the-settings-service-could", { a1: error instanceof Error ? error.message : String(error) }));
1268
1268
  }
1269
1269
  const rows = paramSurfaceRows(sections, PARAM_EXPOSURE);
1270
1270
  const group = paramsMatch[1];
1271
1271
  const selected = group === void 0 ? rows : rows.filter((row) => row.group === group);
1272
- if (group !== void 0 && selected.length === 0) return err(`E-308: unknown parameter group "${group}" — known groups: ${paramGroups(PARAM_EXPOSURE).join(", ")}.`);
1272
+ if (group !== void 0 && selected.length === 0) return err(errorText("e-308-unknown-parameter-group-group", {
1273
+ a1: group,
1274
+ a2: paramGroups(PARAM_EXPOSURE).join(", ")
1275
+ }));
1273
1276
  if (input.endsWith("--json")) return ok(renderParamJson(selected));
1274
1277
  return ok(renderParamRows(selected, { providerMounted: provider !== void 0 }));
1275
1278
  }
1276
1279
  const policySetMatch = /^policy set ([A-Za-z][A-Za-z0-9.-]*) (.+?)(?: --expect (\d+))?$/.exec(input);
1277
1280
  if (policySetMatch) {
1278
1281
  const provider = ctx.get("settings");
1279
- if (provider?.update === void 0 || provider.describe === void 0) return err("E-311: no settings service is mounted, so there is no user layer to write. Mount the settings row (packages/settings/settings-file) and retry; this command never edits cordis.yml — a deployment value belongs to the deployment. /evolution doctor lists the mounted services.");
1282
+ if (provider?.update === void 0 || provider.describe === void 0) return err(errorText("e-311-no-settings-service-is"));
1280
1283
  const rawId = policySetMatch[1] ?? "";
1281
1284
  const rawValue = policySetMatch[2] ?? "";
1282
1285
  let id;
1283
1286
  try {
1284
1287
  id = canonicalWriteId(rawId);
1285
1288
  } catch (error) {
1286
- return err(`E-312: ${error instanceof Error ? error.message : String(error)} — /evolution params lists the canonical ids.`);
1289
+ return err(errorText("e-312-error-instanceof-Error-error", { a1: error instanceof Error ? error.message : String(error) }));
1287
1290
  }
1288
1291
  const entry = PARAM_EXPOSURE.find((candidate) => candidate.id === id);
1289
- if (entry === void 0) return err(`E-313: unknown parameter "${id}" /evolution params lists every registered id with its tier and user layer.`);
1290
- if (entry.tier !== "E3") return err(`E-314: "${id}" is a deployment parameter (tier ${entry.tier}, owner ${entry.owner}) — it is written in cordis.yml on its plugin or policy row, not from a session. /evolution params shows who may write each row.`);
1292
+ if (entry === void 0) return err(errorText("e-313-unknown-parameter-id-evolution", { a1: id }));
1293
+ if (entry.tier !== "E3") return err(errorText("e-314-id-is-a-deployment", {
1294
+ a1: id,
1295
+ a2: entry.tier,
1296
+ a3: entry.owner
1297
+ }));
1291
1298
  const namespace = PARAM_NAMESPACES[entry.owner];
1292
- if (namespace === void 0) return err(`E-315: "${id}" has no user layer — owner ${entry.owner} publishes no settings namespace, so the value stays with the deployment.`);
1299
+ if (namespace === void 0) return err(errorText("e-315-id-has-no-user", {
1300
+ a1: id,
1301
+ a2: entry.owner
1302
+ }));
1293
1303
  let descriptor;
1294
1304
  try {
1295
1305
  descriptor = provider.describe({ redactSecrets: false }).find((candidate) => candidate.ns === namespace);
1296
1306
  } catch (error) {
1297
- return err(`E-307: the settings service could not report its sections (${error instanceof Error ? error.message : String(error)}). /evolution policy set needs the current revision to write safely; run /evolution doctor to see which services are mounted.`);
1307
+ return err(errorText("e-307-the-settings-service-could-2", { a1: error instanceof Error ? error.message : String(error) }));
1298
1308
  }
1299
- if (descriptor === void 0) return err(`E-316: namespace "${namespace}" is not registered — owner ${entry.owner} is not mounted in this composition, so its parameters cannot be written from here. /evolution params marks those rows 'unregistered'.`);
1309
+ if (descriptor === void 0) return err(errorText("e-316-namespace-namespace-is-not", {
1310
+ a1: namespace,
1311
+ a2: entry.owner
1312
+ }));
1300
1313
  const current = descriptor.value;
1301
1314
  const resolved = typeof current === "object" && current !== null ? current : void 0;
1302
1315
  const expectedRaw = policySetMatch[3];
1303
1316
  const expected = expectedRaw === void 0 ? descriptor.revision : Number(expectedRaw);
1304
- if (expectedRaw !== void 0 && descriptor.revision !== void 0 && Number(expectedRaw) !== descriptor.revision) return err(`E-309: revision conflictyou sent ${expectedRaw}, the document stands at ${descriptor.revision}. Re-read with /evolution params --json and retry.`);
1317
+ if (expectedRaw !== void 0 && descriptor.revision !== void 0 && Number(expectedRaw) !== descriptor.revision) return err(errorText("e-309-revision-conflict-you-sent", {
1318
+ a1: expectedRaw,
1319
+ a2: descriptor.revision
1320
+ }));
1305
1321
  const value = parseParamValue(rawValue);
1306
1322
  try {
1307
1323
  await provider.update(namespace, { [id]: value }, expected);
1308
1324
  } catch (error) {
1309
1325
  const code = error?.code;
1310
1326
  const message = error instanceof Error ? error.message : String(error);
1311
- if (code === "SETTINGS_CONFLICT") return err(`E-309: revision conflict — ${message}. Re-read with /evolution params --json and retry.`);
1312
- return err(`E-310: the settings service refused the write: ${message} — the owning plugin's rule stands (a cross-field pair, or a cap that may only be tightened). /evolution params shows the current value.`);
1327
+ if (code === "SETTINGS_CONFLICT") return err(errorText("e-309-revision-conflict-message-Re", { a1: message }));
1328
+ return err(errorText("e-310-the-settings-service-refused", { a1: message }));
1313
1329
  }
1314
1330
  return ok(renderPolicySet({
1315
1331
  id,
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.6.1",
4
+ "version": "0.8.0",
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.6.1",
31
- "@lmzhen/dsh-evolution-core": "^0.6.1",
32
- "@lmzhen/dsh-evolution-maintenance": "^0.6.1"
30
+ "@lmzhen/dsh-evolution-approval": "^0.8.0",
31
+ "@lmzhen/dsh-evolution-core": "^0.8.0",
32
+ "@lmzhen/dsh-evolution-maintenance": "^0.8.0"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@lmzhen/dsh-evolution-agent-preset": "^0.6.1"
35
+ "@lmzhen/dsh-evolution-agent-preset": "^0.8.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.1",