@lmzhen/dsh-evolution-commands 0.8.0 → 0.10.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.
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, errorText, 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_GROUP_LABELS, PARAM_NAMESPACES, appendEvolutionEvent, assertSkillsRootAliasRetired, buildLearnPrompt, canonicalWriteId, clampedNumber, composePresetComposition, errorText, eventsFile, evolutionRoot, isDeprecatedParamId, isMissingPath, newSkillLibrary, orderVersions, 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";
@@ -643,15 +643,46 @@ function renderValue(value) {
643
643
  if (typeof value === "string") return value;
644
644
  return JSON.stringify(value);
645
645
  }
646
+ const WIDE_CHAR = new RegExp("[" + [
647
+ "ᄀ-ᅟ",
648
+ "⺀-〾",
649
+ "ぁ-㏿",
650
+ "㐀-䶿",
651
+ "一-鿿",
652
+ "가-힣",
653
+ "豈-﫿",
654
+ "︰-﹯",
655
+ "＀-⦆",
656
+ "¢-₩"
657
+ ].join("") + "]");
646
658
  /**
647
- * Render the rows as fixed-width text (one parameter per line, never truncated).
648
- * @param rows - rows from {@link paramSurfaceRows}.
649
- * @param options - whether the settings service answered at all.
650
- * @returns the command text.
659
+ * Pad one cell to a display width, counting CJK characters as two columns.
660
+ * @param text - the cell text.
661
+ * @param columns - the target width in terminal columns.
662
+ * @returns the text plus at least one space, so columns never touch.
651
663
  */
664
+ function pad(text, columns) {
665
+ let used = 0;
666
+ for (const char of text) used += WIDE_CHAR.test(char) ? 2 : 1;
667
+ return text + " ".repeat(Math.max(1, columns - used));
668
+ }
669
+ /** Settings timing, in the operator's words. */
670
+ const APPLIES_LABELS = Object.freeze({
671
+ live: "立即",
672
+ restart: "重启后",
673
+ none: "不生效"
674
+ });
675
+ /** Where a row's effective value comes from, in the operator's words. */
676
+ const SOURCE_LABELS = Object.freeze({
677
+ user: "我改过",
678
+ deployment: "默认",
679
+ unregistered: "不可改"
680
+ });
681
+ /** What the E-tiers mean, so the count line reads without the docs. */
682
+ const TIER_LEGEND = "档位:E0 固定/E1 只读/E2 安装时定/E3 你可改/E4 装完固化。";
652
683
  function renderParamRows(rows, options) {
653
- const header = "PARAMETER".padEnd(36) + "GROUP".padEnd(12) + "TIER".padEnd(5) + "APPLIES".padEnd(9) + "SOURCE".padEnd(14) + "VALUE";
654
- const body = rows.map((row) => row.id.padEnd(36) + row.group.padEnd(12) + row.tier.padEnd(5) + row.applies.padEnd(9) + row.source.padEnd(14) + renderValue(row.value));
684
+ const header = pad("参数", 36) + pad("分组", 12) + pad("档位", 6) + pad("生效", 8) + pad("来源", 10) + "当前值";
685
+ const body = rows.map((row) => pad(row.id, 36) + pad(PARAM_GROUP_LABELS[row.group], 12) + pad(row.tier, 6) + pad(APPLIES_LABELS[row.applies] ?? row.applies, 8) + pad(SOURCE_LABELS[row.source] ?? row.source, 10) + renderValue(row.value));
655
686
  const tiers = [
656
687
  "E0",
657
688
  "E1",
@@ -660,13 +691,14 @@ function renderParamRows(rows, options) {
660
691
  "E4"
661
692
  ].map((tier) => `${tier} ${rows.filter((row) => row.tier === tier).length}`).join(" / ");
662
693
  const userRows = rows.filter((row) => row.source === "user").length;
663
- const overrides = options.providerMounted ? `${userRows} overridden by the user.` : "overrides UNKNOWN (no settings provider).";
664
- const note = options.providerMounted ? "SOURCE: user = you set it in settings; deployment = the row/policy value applies; unregistered = the owner publishes no user layer." : "settings provider not mounted — the user layer is unavailable, so every row shows its deployment face (this is NOT \"no overrides\").";
694
+ const counts = options.providerMounted ? `共 ${rows.length} 个参数:${tiers};你改过的:${userRows} 项。` : `共 ${rows.length} 个参数:${tiers};是否改过:未知(没有设置服务)。`;
695
+ const note = options.providerMounted ? "来源:我改过=你在设置里改的;默认=安装时的配置;不可改=该功能不提供用户可改项。" : "设置服务未挂载——读不到你改过的值,所以每行显示的都是安装时的配置(这不等于「你没改过」)。";
665
696
  return [
666
697
  header,
667
698
  ...body,
668
699
  "",
669
- `${rows.length} parameter(s): ${tiers}; ${overrides}`,
700
+ counts,
701
+ TIER_LEGEND,
670
702
  note
671
703
  ].join("\n");
672
704
  }
@@ -709,79 +741,87 @@ function renderParamJson(rows) {
709
741
  const COMMAND_ENTRIES = [
710
742
  {
711
743
  usage: "pending [--detail]",
712
- summary: "list staged evolution writes (--detail shows staged args)"
744
+ summary: "列出暂存的写入(--detail 显示参数)"
713
745
  },
714
746
  {
715
747
  usage: "approve <id>",
716
- summary: "replay an approved staged write through its runner"
748
+ summary: "执行一条已批准的写入"
717
749
  },
718
750
  {
719
751
  usage: "reject <id>",
720
- summary: "drop a staged write without running it"
752
+ summary: "丢弃一条暂存写入,不执行"
721
753
  },
722
754
  {
723
755
  usage: "release <id>",
724
- summary: "return an orphaned EXECUTING write to the pending window (verify the effect first, then approve or reject)"
756
+ summary: "把卡在执行中的写入放回待批窗口(先确认效果再批准 or reject)"
725
757
  },
726
758
  {
727
759
  usage: "doctor [--json]",
728
- summary: "read-only self-check: install form, conflicts, env, services (--json feeds scripts)"
760
+ summary: "只读自检:安装形态、冲突、环境、服务(--json 供脚本用)"
729
761
  },
730
762
  {
731
763
  usage: "curator run|pause|resume|status|report|scope",
732
- summary: "run one curation pass, control or inspect automatic curation"
764
+ summary: "立即运行一次自动整理,或暂停/恢复/查看状态"
733
765
  },
734
766
  {
735
767
  usage: "mutations",
736
- summary: "list skill-mutation audit records"
768
+ summary: "列出技能改动记录"
737
769
  },
738
770
  {
739
771
  usage: "restore",
740
- summary: "restore skills from the latest snapshot"
772
+ summary: "从最近的快照恢复技能"
741
773
  },
742
774
  {
743
775
  usage: "consolidate <target> <sources...> [--plan <runId>]",
744
- summary: "merge source skills into a target umbrella skill"
776
+ summary: "把若干技能合并进一个总括技能"
745
777
  },
746
778
  {
747
779
  usage: "skill restore <name>",
748
- summary: "restore one archived skill by name"
780
+ summary: "按名字恢复一个已归档的技能"
781
+ },
782
+ {
783
+ usage: "skill history <name>",
784
+ summary: "列出某个技能的内容版本(含时间、动作、字符数、哈希前缀)"
785
+ },
786
+ {
787
+ usage: "skill undo <name> [--to v<N>]",
788
+ summary: "把某个技能的内容回退到某一版(默认上一版;只动内容,撤销本身也记为一版)"
749
789
  },
750
790
  {
751
791
  usage: "skills health",
752
- summary: "structure-health verdicts for the skill library"
792
+ summary: "技能库结构体检"
753
793
  },
754
794
  {
755
795
  usage: "skills refresh",
756
- summary: "drop the catalog caches and re-read the tree"
796
+ summary: "丢弃目录缓存并重新读取技能树"
757
797
  },
758
798
  {
759
799
  usage: "learn [request]",
760
- summary: "send a learning request to this session"
800
+ summary: "向本会话发一条学习请求"
761
801
  },
762
802
  {
763
803
  usage: "maintain [--timeout=<ms> | --facts]",
764
- summary: "run a maintenance scan (--facts: 0-token preview)"
804
+ summary: "运行一次维护扫描(--facts 为 0 token 预览)"
765
805
  },
766
806
  {
767
807
  usage: "policy set <id> <value> [--expect <revision>]",
768
- summary: "write one user-writable parameter through the settings service (E3 only; E1/E2 stay in cordis.yml)"
808
+ summary: "通过设置服务写入一个你可改的参数 (E3 only; E1/E2 stay in cordis.yml)"
769
809
  },
770
810
  {
771
811
  usage: "params [--group <name>] [--json]",
772
- summary: "list every registered parameter: group, tier, timing, source and value (--json feeds scripts)"
812
+ summary: "列出全部参数:分组、档位、生效时机、来源与当前值(--json feeds scripts)"
773
813
  },
774
814
  {
775
815
  usage: "preset install [--base <name>[,<name>...]]",
776
- summary: "generate one Evolution agent preset per named base into the user root (bases from the agent package's bases.json)"
816
+ summary: "按指定基础生成自进化 Agent 预设到用户 root (bases from the agent package's bases.json)"
777
817
  },
778
818
  {
779
819
  usage: "restructure <name> \"<heading>\" <to_file> [--plan <runId>]",
780
- summary: "move a body section into a references/ file"
820
+ summary: "把技能正文的一节移到 references 文件"
781
821
  },
782
822
  {
783
823
  usage: "replay",
784
- summary: "compare plan outcomes across sessions and restarts (backfilled from the activity store)"
824
+ summary: "比较不同会话与重启之间的计划结果(从活动记录回填)"
785
825
  }
786
826
  ];
787
827
  /** The input-declaration hint (single line, ` | `-separated). */
@@ -818,7 +858,7 @@ function apply(ctx, rawConfig = {}) {
818
858
  const commands = commandCtx.commands;
819
859
  const evolutionCommand = {
820
860
  name: "evolution",
821
- description: "Self-evolution status and approval controls",
861
+ description: "自进化:状态与待批准写入",
822
862
  recordInput: false,
823
863
  input: { hint: renderHint() },
824
864
  run: async (invocation) => {
@@ -989,6 +1029,32 @@ function apply(ctx, rawConfig = {}) {
989
1029
  };
990
1030
  return result.ok ? ok(result.message) : err(result.message);
991
1031
  }
1032
+ if (input.startsWith("skill history ")) {
1033
+ const name = input.slice(14).trim();
1034
+ if (!name) return err("Usage: /evolution skill history <name>");
1035
+ const curator = ctx.get("evolutionCurator");
1036
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
1037
+ const versions = await curator.history(name);
1038
+ if (versions.length === 0) return ok(`No content versions are recorded for "${name}" yet — they appear after the next write through skill_manage.`);
1039
+ const ordered = orderVersions(versions);
1040
+ const rows = ordered.map((entry) => `v${entry.v}\t${entry.at}\t${entry.action}\t${entry.chars} chars\t${entry.hash.slice(0, 12)}`);
1041
+ return ok([`Content versions for "${name}" (oldest first, ${ordered.length} of at most the retained count):`, ...rows].join("\n"));
1042
+ }
1043
+ if (input.startsWith("skill undo ")) {
1044
+ const rest = input.slice(11).trim();
1045
+ const toMatch = /--to\s+v?(\d+)\s*$/.exec(rest);
1046
+ const name = (toMatch?.index === void 0 ? rest : rest.slice(0, toMatch.index)).trim();
1047
+ if (!name) return err("Usage: /evolution skill undo <name> [--to v<N>]");
1048
+ const skillUndoRefusal = unreplayableWriteRefusal("skill undo");
1049
+ if (skillUndoRefusal) return skillUndoRefusal;
1050
+ const curator = ctx.get("evolutionCurator");
1051
+ const target = toMatch?.[1] === void 0 ? void 0 : Number(toMatch[1]);
1052
+ const result = curator ? await (target === void 0 ? curator.undo(name) : curator.undo(name, target)) : {
1053
+ ok: false,
1054
+ message: errorText("e-302-curator-service-not-mounted")
1055
+ };
1056
+ return result.ok ? ok(result.message) : err(result.message);
1057
+ }
992
1058
  if (input === "skills health") {
993
1059
  const curator = ctx.get("evolutionCurator");
994
1060
  if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
@@ -1341,7 +1407,7 @@ function apply(ctx, rawConfig = {}) {
1341
1407
  if (input === "doctor --json") return ok(JSON.stringify(report, null, 2));
1342
1408
  return ok(renderDoctorText(report));
1343
1409
  }
1344
- return ok(`Evolution: memory, skills, review, curator — self-evolution status, approval and maintenance.\n${renderHelpText()}`);
1410
+ return ok(`自进化:记忆、技能、会话回顾、技能整理——状态、待批准写入与维护。\n${renderHelpText()}`);
1345
1411
  },
1346
1412
  handler: (invocation) => evolutionCommand.run(invocation).catch((error) => ({
1347
1413
  kind: "error",
@@ -52,12 +52,6 @@ export declare function paramSurfaceRows(sections: ReadonlyMap<string, ParamSect
52
52
  * @returns distinct group names.
53
53
  */
54
54
  export declare function paramGroups(entries?: readonly ParamExposure[]): string[];
55
- /**
56
- * Render the rows as fixed-width text (one parameter per line, never truncated).
57
- * @param rows - rows from {@link paramSurfaceRows}.
58
- * @param options - whether the settings service answered at all.
59
- * @returns the command text.
60
- */
61
55
  export declare function renderParamRows(rows: readonly ParamSurfaceRow[], options: {
62
56
  providerMounted: boolean;
63
57
  }): string;
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.8.0",
4
+ "version": "0.10.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.8.0",
31
- "@lmzhen/dsh-evolution-core": "^0.8.0",
32
- "@lmzhen/dsh-evolution-maintenance": "^0.8.0"
30
+ "@lmzhen/dsh-evolution-approval": "^0.10.0",
31
+ "@lmzhen/dsh-evolution-core": "^0.10.0",
32
+ "@lmzhen/dsh-evolution-maintenance": "^0.10.0"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@lmzhen/dsh-evolution-agent-preset": "^0.8.0"
35
+ "@lmzhen/dsh-evolution-agent-preset": "^0.10.0"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.1",