@lmzhen/dsh-evolution-commands 0.30.36 → 0.30.38

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 +45 -59
  2. package/package.json +5 -5
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- import { DEFAULT_SKILL_LIMITS, DIAGNOSTICS_FACT_KEYS, DIAGNOSTICS_RUNTIME_KEYS, DIAGNOSTICS_VIEW_SCHEMA_VERSION, EVOLUTION_PRESET_IDS, MAINTAINER_PRESET_ID, MAX_TIMER_DELAY_MS, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PLATFORM_LINE, REVIEWER_PRESET_ID, absentHostServices, appendEvolutionEvent, applyNamespaceMigration, asRecord, assertSkillsRootAliasRetired, atomicWriteFiles, authorityFrom, buildLearnPrompt, canonicalWriteId, clampedNumber, classifyNamespaceMigration, classifySessionEvent, contentHash, createSubagentChannel, elapsedSince, errorText, eventsFile, evolutionHome, evolutionRoot, findOwnerCandidate, hostSurface, isDeprecatedParamId, isPresent, isRecord, isUnknown, liveBool, newRunRegistry, newSkillLibrary, ownerCandidatesFromSummaries, paramSettingsId, parseFrontmatter, partitionVersions, planExtract, planNamespaceMigration, policyStageLimits, probeMtime, probeText, proposalFromPlan, readExternalSupport, reportsSweepLockTarget, resolveParamId, resolveRootConfig, scopedPackageName, scopedProbeReport, sweepReports, transactIo, writeProposalReport } from "@lmzhen/dsh-evolution-core";
2
+ import { DEFAULT_SKILL_LIMITS, DIAGNOSTICS_FACT_KEYS, DIAGNOSTICS_RUNTIME_KEYS, DIAGNOSTICS_VIEW_SCHEMA_VERSION, EVOLUTION_PRESET_IDS, MAINTAINER_PRESET_ID, MAX_TIMER_DELAY_MS, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PLATFORM_LINE, REVIEWER_PRESET_ID, absentHostServices, appendEvolutionEvent, applyNamespaceMigration, asRecord, assertSkillsRootAliasRetired, atomicWriteFiles, authorityFrom, buildExtractPlanFromLibrary, buildLearnPrompt, canonicalWriteId, clampedNumber, classifyNamespaceMigration, classifySessionEvent, createSubagentChannel, elapsedSince, errorText, eventsFile, evolutionHome, evolutionRoot, hostSurface, isDeprecatedParamId, isPresent, isRecord, isUnknown, liveBool, newRunRegistry, newSkillLibrary, paramSettingsId, partitionVersions, planNamespaceMigration, policyStageLimits, probeMtime, probeText, proposalFromPlan, reportsSweepLockTarget, resolveParamId, resolveRootConfig, scopedPackageName, scopedProbeReport, sweepReports, transactIo, writeProposalReport } from "@lmzhen/dsh-evolution-core";
3
3
  import { HOST_ROUTES, json, methodNotAllowed, registerHostRoutes } from "@lmzhen/dsh-evolution-core/http-routes";
4
4
  import { existsSync, readFileSync, readdirSync } from "node:fs";
5
5
  import { dirname, join } from "node:path";
@@ -1996,6 +1996,11 @@ const COMMAND_ENTRIES = [
1996
1996
  usage: "skill unpin <name>",
1997
1997
  summary: "解钉一个技能:撤销上面的冻结(立即生效、不走审批)"
1998
1998
  },
1999
+ {
2000
+ readonly: false,
2001
+ usage: "propose",
2002
+ summary: "为 body-oversize 技能写 keep 提议(不自动拆分;同一 reports store)"
2003
+ },
1999
2004
  {
2000
2005
  readonly: true,
2001
2006
  usage: "skills health",
@@ -2063,8 +2068,8 @@ const COMMAND_ENTRIES = [
2063
2068
  },
2064
2069
  {
2065
2070
  readonly: false,
2066
- usage: "extract <source> <target> \"<heading>\" [--start <line>] [--end <line>] [--plan <runId>]",
2067
- summary: "把源技能的可复用部分提取到已有类级 owner(多根一次事务)"
2071
+ usage: "extract <source> <target> \"<heading>\" [--start <line>] [--end <line>] [--outcome extract|mixed] [--apply]",
2072
+ summary: "预览把源技能的可复用部分提取到已有类级 owner;默认 plan,--apply 才写(多根一次事务)"
2068
2073
  },
2069
2074
  {
2070
2075
  readonly: true,
@@ -2114,7 +2119,8 @@ const VALUE_FLAGS = [
2114
2119
  "timeout",
2115
2120
  "plan",
2116
2121
  "start",
2117
- "end"
2122
+ "end",
2123
+ "outcome"
2118
2124
  ];
2119
2125
  /**
2120
2126
  * Narrow a parse result: true when it is the syntax failure rather than a command.
@@ -2711,21 +2717,25 @@ function apply(ctx, rawConfig = {}) {
2711
2717
  const versions = await curator.history(name);
2712
2718
  if (versions.length === 0) return ok(`技能 "${name}" 还没有内容版本:下一次经 skill_manage 写入后才会出现。`);
2713
2719
  const groups = partitionVersions(versions);
2714
- const row = (entry) => `v${entry.v}\t${entry.at}\t${entry.action}\t${entry.chars} chars\t${entry.hash.slice(0, 12)}`;
2720
+ const row = (entry) => `v${entry.v}\t${entry.at}\t${entry.action}\t${entry.chars} chars\t${entry.hash.slice(0, 12)}${entry.unitId === void 0 ? "" : `\tunit:${entry.unitId}`}`;
2715
2721
  const lines = [`技能 "${name}" 的内容版本(由旧到新,${groups.content.length} 条,最多保留这么多):`, ...groups.content.map(row)];
2716
2722
  if (groups.support.length > 0) lines.push("支撑文件版本(仅在历史里 —— 撤销恢复的是 SKILL.md,不是这些字节):", ...groups.support.map(row));
2717
2723
  return ok(lines.join("\n"));
2718
2724
  }
2719
2725
  if (input.startsWith("skill undo ")) {
2720
2726
  const rest = input.slice(11).trim();
2721
- const toMatch = /--to\s+v?(\d+)\s*$/.exec(rest);
2722
- const name = (toMatch?.index === void 0 ? rest : rest.slice(0, toMatch.index)).trim();
2723
- if (!name) return err("用法:/evolution skill undo <name> [--to v<N>]");
2727
+ const toMatch = /--to\s+v?(\d+)/.exec(rest);
2728
+ const unitMatch = /--unit\s+(\S+)/.exec(rest);
2729
+ const marker = [toMatch?.index, unitMatch?.index].filter((index) => index !== void 0).sort((a, b) => a - b)[0];
2730
+ const name = (marker === void 0 ? rest : rest.slice(0, marker)).trim();
2731
+ if (!name) return err("用法:/evolution skill undo <name> [--to v<N> | --unit <id>]");
2732
+ if (toMatch !== null && unitMatch !== null) return err("--to 与 --unit 不能同时使用");
2724
2733
  const skillUndoRefusal = unreplayableWriteRefusal("skill undo");
2725
2734
  if (skillUndoRefusal) return skillUndoRefusal;
2726
2735
  const curator = ctx.get("evolutionCurator");
2727
2736
  const target = toMatch?.[1] === void 0 ? void 0 : Number(toMatch[1]);
2728
- const result = curator ? await (target === void 0 ? curator.undo(name) : curator.undo(name, target)) : {
2737
+ const unitId = unitMatch?.[1];
2738
+ const result = curator ? unitId !== void 0 ? await curator.undoUnit(name, unitId) : target === void 0 ? await curator.undo(name) : await curator.undo(name, target) : {
2729
2739
  ok: false,
2730
2740
  message: errorText("e-302-curator-service-not-mounted")
2731
2741
  };
@@ -2749,6 +2759,12 @@ function apply(ctx, rawConfig = {}) {
2749
2759
  }).setPinned(name, pinned);
2750
2760
  return result.ok ? ok(result.message) : err(result.message);
2751
2761
  }
2762
+ if (input === "propose") {
2763
+ const curator = ctx.get("evolutionCurator");
2764
+ if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
2765
+ const result = await curator.proposeKeeps();
2766
+ return result.ok ? ok(result.message) : err(result.message);
2767
+ }
2752
2768
  if (input === "skills health") {
2753
2769
  const curator = ctx.get("evolutionCurator");
2754
2770
  if (!curator) return err(errorText("e-302-curator-service-not-mounted"));
@@ -3161,13 +3177,15 @@ function apply(ctx, rawConfig = {}) {
3161
3177
  }
3162
3178
  if (input.startsWith("extract ")) {
3163
3179
  const extractParsed = parseSubcommand(rawInputTrimmed);
3164
- if (isParseFailure(extractParsed) || extractParsed.name !== "extract") return err("用法:/evolution extract <source> <target> \"<## heading>\" [--start <line>] [--end <line>]");
3180
+ if (isParseFailure(extractParsed) || extractParsed.name !== "extract") return err("用法:/evolution extract <source> <target> \"<heading>\" [--start <line>] [--end <line>]");
3165
3181
  const source = extractParsed.positionals[0] ?? "";
3166
3182
  const target = extractParsed.positionals[1] ?? "";
3167
3183
  const heading = extractParsed.positionals[2] ?? "";
3168
- if (source === "" || target === "" || heading === "") return err("用法:/evolution extract <source> <target> \"<## heading>\" [--start <line>] [--end <line>]");
3184
+ if (source === "" || target === "" || heading === "") return err("用法:/evolution extract <source> <target> \"<heading>\" [--start <line>] [--end <line>]");
3169
3185
  const startFlag = extractParsed.flags.get("start");
3170
3186
  const endFlag = extractParsed.flags.get("end");
3187
+ const outcomeFlag = extractParsed.flags.get("outcome");
3188
+ if (outcomeFlag !== void 0 && outcomeFlag !== "extract" && outcomeFlag !== "mixed") return err("--outcome 只能是 extract 或 mixed。");
3171
3189
  const start = typeof startFlag === "string" ? startFlag : void 0;
3172
3190
  const end = typeof endFlag === "string" ? endFlag : void 0;
3173
3191
  const ioRegistry = ctx.get("evolutionIo");
@@ -3183,56 +3201,24 @@ function apply(ctx, rawConfig = {}) {
3183
3201
  ...policyStageLimits(ctx.get("evolutionPolicy")?.get?.())
3184
3202
  }
3185
3203
  });
3186
- const sourceMd = await library.read(source);
3187
- const targetMd = await library.read(target);
3188
- if (sourceMd === null) return err("技能「" + source + "」不存在。");
3189
- if (targetMd === null) return err("目标技能「" + target + "」不存在;owner-gap 不自动 create。");
3190
- if (source === target) return err("source 与 target 不能相同。");
3191
- const candidate = findOwnerCandidate(ownerCandidatesFromSummaries(source, await library.list({
3192
- withContent: true,
3193
- withFrontmatter: true
3194
- })), target);
3195
- if (candidate === void 0) return err("目标「" + target + "」不是可用的 owner candidate(描述为空或已排除);owner-gap,不自动 create。");
3196
- if (candidate.protectionUnknown || candidate.protectedBy !== null) return err("目标「" + target + "」受保护(" + (candidate.protectedBy ?? "未知") + "),拒绝提取。");
3197
- const sourceParsed = parseFrontmatter(sourceMd);
3198
- if (sourceParsed === null) return err("技能「" + source + "」没有可读取的正文。");
3199
- const sourceFiles = await library.listSupportFiles(source);
3200
- const targetFiles = await library.listSupportFiles(target);
3201
- if (!isPresent(sourceFiles) || !isPresent(targetFiles)) return err("支撑文件列表不可读;拒绝在不确定的闭包上提取。");
3202
- const external = await readExternalSupport(sourceParsed.body, join(library.root, source), dirname(library.root), targetFiles.value);
3203
- if (!external.ok) return err(external.message);
3204
- const plan = planExtract({
3205
- body: sourceParsed.body,
3206
- sourceName: source,
3207
- targetName: target,
3208
- targetExists: true,
3209
- outcome: start !== void 0 || end !== void 0 ? "mixed" : "extract",
3210
- owner: {
3211
- mode: "covered",
3212
- name: target,
3213
- triggerCovers: true,
3214
- outcomeCovers: true,
3215
- reason: "explicit target"
3216
- },
3217
- requests: [{
3204
+ const built = await buildExtractPlanFromLibrary(library, {
3205
+ source,
3206
+ items: [{
3218
3207
  heading,
3219
3208
  ...start === void 0 ? {} : { start },
3220
- ...end === void 0 ? {} : { end }
3209
+ ...end === void 0 ? {} : { end },
3210
+ to_skill: target
3221
3211
  }],
3222
- sourceFiles: sourceFiles.value,
3223
- targetFiles: targetFiles.value,
3224
- external: external.files,
3225
- anchors: {
3226
- sourceMdSha256: contentHash(sourceMd),
3227
- targetMdSha256: contentHash(targetMd)
3228
- }
3212
+ ...outcomeFlag === void 0 ? {} : { outcome: outcomeFlag }
3229
3213
  });
3230
- if (!plan.ok) return err(plan.message);
3231
- if (extractParsed.flags.has("plan")) {
3232
- const proposal = proposalFromPlan(plan.plan, { runId: "cli-" + Date.now().toString(36) });
3214
+ if (!built.ok) return err(built.message);
3215
+ const plan = built.plan;
3216
+ const warnings = built.warnings === void 0 || built.warnings.length === 0 ? "" : "\n" + built.warnings.join("\n");
3217
+ if (!extractParsed.flags.has("apply")) {
3218
+ const proposal = proposalFromPlan(plan, { runId: "cli-" + Date.now().toString(36) });
3233
3219
  const io = ctx.get("evolutionIo")?.provider();
3234
3220
  if (io !== void 0) await writeProposalReport(io, proposal).catch(() => {});
3235
- return ok("提取计划(未写入):" + plan.plan.parts.map((part) => "## " + part.heading).join(", ") + " -> " + target);
3221
+ return ok("提取计划(未写入;默认 plan,--apply 才写):" + plan.parts.map((part) => "## " + part.heading).join(", ") + " -> " + target + "\n提取判定:outcome=" + plan.outcome + ";target=" + target + ";owner=covered by " + target + warnings);
3236
3222
  }
3237
3223
  if (approval) {
3238
3224
  const session = invocationAgent?.session;
@@ -3266,17 +3252,17 @@ function apply(ctx, rawConfig = {}) {
3266
3252
  });
3267
3253
  if (decision.action === "staged") return ok(decision.message);
3268
3254
  }
3269
- const result = await library.extract(plan.plan, "foreground");
3255
+ const result = await library.extract(plan, "foreground");
3270
3256
  if (!result.ok) return err(result.message);
3271
- return ok(result.message);
3257
+ return ok(result.message + "\n提取判定:outcome=" + plan.outcome + ";target=" + target + warnings);
3272
3258
  }
3273
3259
  if (input.startsWith("restructure ")) {
3274
3260
  const restParsed = parseSubcommand(rawInputTrimmed);
3275
- if (isParseFailure(restParsed) || restParsed.name !== "restructure") return err("用法:/evolution restructure <name> \"<## heading>\" <to_file>");
3261
+ if (isParseFailure(restParsed) || restParsed.name !== "restructure") return err("用法:/evolution restructure <name> \"<heading>\" <to_file>");
3276
3262
  const name = restParsed.positionals[0] ?? "";
3277
3263
  const heading = restParsed.positionals[1] ?? "";
3278
3264
  const toFile = restParsed.positionals[2] ?? "";
3279
- if (name === "" || heading === "" || toFile === "") return err("用法:/evolution restructure <name> \"<## heading>\" <to_file>");
3265
+ if (name === "" || heading === "" || toFile === "") return err("用法:/evolution restructure <name> \"<heading>\" <to_file>");
3280
3266
  const planFlag = restParsed.flags.get("plan");
3281
3267
  const planRunId = typeof planFlag === "string" ? planFlag : void 0;
3282
3268
  if (!toFile.startsWith("references/")) return err("to_file 必须位于 references/ 之下(日志或明细的落地位置)。");
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.30.36",
4
+ "version": "0.30.38",
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.5",
30
- "@lmzhen/dsh-evolution-approval": "^0.30.36",
31
- "@lmzhen/dsh-evolution-core": "^0.30.36",
32
- "@lmzhen/dsh-evolution-maintenance": "^0.30.36"
30
+ "@lmzhen/dsh-evolution-approval": "^0.30.38",
31
+ "@lmzhen/dsh-evolution-core": "^0.30.38",
32
+ "@lmzhen/dsh-evolution-maintenance": "^0.30.38"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@lmzhen/dsh-evolution-agent-preset": "^0.30.36"
35
+ "@lmzhen/dsh-evolution-agent-preset": "^0.30.38"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.5",