@lmzhen/dsh-evolution-core 0.23.0 → 0.25.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
@@ -473,6 +473,13 @@ const DEFAULT_CURATOR_INTERVAL_HOURS = 168;
473
473
  const DEFAULT_MIN_IDLE_HOURS = 2;
474
474
  const DEFAULT_STALE_AFTER_DAYS = 30;
475
475
  const DEFAULT_ARCHIVE_AFTER_DAYS = 90;
476
+ /**
477
+ * Support-file count above which `references-sprawl` warns. **NOT CALIBRATED YET** — 60 is a
478
+ * placeholder: the platform corpus is empty (no platform skills available here), so the admission
479
+ * band cannot be checked. Until `gen-health-baseline` reports both corpora, the rule is
480
+ * REPORT-ONLY: its count must not be shown as a number on any surface (plan G4 ③).
481
+ */
482
+ const SUPPORT_SPRAWL_FILES = 60;
476
483
  const DEFAULT_MEMORY_CHAR_LIMIT = 2200;
477
484
  const DEFAULT_USER_CHAR_LIMIT = 1375;
478
485
  /** Consolidation-failure backoff cap, shared by MemoryStore and memory-files' Config default. */
@@ -531,7 +538,7 @@ const PLATFORM_CATALOG_DESCRIPTION_DEFAULT = 500;
531
538
  * be copied into validateFrontmatter AND the patch path, and neither copy named
532
539
  * where the content should go — the upstream cap message names the destination
533
540
  * directories, and that is the part a model actually acts on. */
534
- const CONTENT_SPLIT_HINT = "Consider splitting into a smaller SKILL.md with supporting files in references/ or templates.";
541
+ const CONTENT_SPLIT_HINT = "建议拆成更小的 SKILL.md,把支撑文件放进 references/ 或 templates/。";
535
542
  /** V27 G2.4: the largest millisecond delay a timer accepts. `AbortSignal.timeout`
536
543
  * (and `setTimeout`) coerce anything larger to 1ms after a Node warning, so a
537
544
  * timeout configured above this ceiling silently collapses to "immediately
@@ -1052,16 +1059,16 @@ function shrinksOverLimit(next, current, limit) {
1052
1059
  }
1053
1060
  function validateFrontmatter(content, expectedName, limits = DEFAULT_SKILL_LIMITS, current) {
1054
1061
  const parsed = parseFrontmatter(content);
1055
- if (!parsed) return "SKILL.md must start with a `---` line, close the frontmatter with another exact `---` line (only a trailing `\\r` is tolerated), and include a body below it.";
1062
+ if (!parsed) return "SKILL.md 必须以 `---` 行开头,用另一个完全相同的 `---` 行结束 frontmatter(只容忍结尾的 `\\r`),并在其下包含正文。";
1056
1063
  const unreadable = parsed.platformStringSplit.filter((entry) => entry.kind === "sequence" || entry.kind === "mapping");
1057
- if (unreadable.length > 0) return `Frontmatter field ${unreadable.map((entry) => `"${entry.key}" (a YAML ${entry.kind})`).join(", ")} must be a string: the platform skill catalog reads such a field as absent and ignores the whole file. Write plain text, or wrap the value in double quotes to keep it literally.`;
1058
- if (!parsed.frontmatter.name) return "Frontmatter must include a name field.";
1059
- if (!SKILL_NAME_RE.test(parsed.frontmatter.name)) return `Invalid skill name "${parsed.frontmatter.name}" — use lowercase letters, digits, and hyphens.`;
1060
- if (parsed.frontmatter.name.length > limits.maxNameLength) return `Skill name exceeds ${limits.maxNameLength} characters.`;
1061
- if (expectedName && parsed.frontmatter.name !== expectedName) return `Frontmatter name "${parsed.frontmatter.name}" does not match target skill "${expectedName}".`;
1062
- if (!parsed.frontmatter.description) return "Frontmatter must include a description field.";
1063
- if (parsed.frontmatter.description.length > limits.maxDescriptionLength) return `Description exceeds ${limits.maxDescriptionLength} characters.`;
1064
- if (exceedsContentLimit(content, limits.maxSkillContentChars) && !shrinksOverLimit(content, current, limits.maxSkillContentChars)) return `SKILL.md content exceeds ${limits.maxSkillContentChars} characters. ${CONTENT_SPLIT_HINT}`;
1064
+ if (unreadable.length > 0) return `Frontmatter 字段 ${unreadable.map((entry) => `"${entry.key}" (a YAML ${entry.kind})`).join(", ")} 必须是字符串:平台技能目录会把这种字段当作缺失,并忽略整个文件。请写成纯文本,或用双引号包住取值以保持字面内容。`;
1065
+ if (!parsed.frontmatter.name) return "Frontmatter 必须包含 name 字段。";
1066
+ if (!SKILL_NAME_RE.test(parsed.frontmatter.name)) return `技能名「${parsed.frontmatter.name}」不合法:请只用小写字母、数字和连字符。`;
1067
+ if (parsed.frontmatter.name.length > limits.maxNameLength) return `技能名超过 ${limits.maxNameLength} 个字符。`;
1068
+ if (expectedName && parsed.frontmatter.name !== expectedName) return `Frontmatter 里的 name「${parsed.frontmatter.name}」与目标技能「${expectedName}」不一致。`;
1069
+ if (!parsed.frontmatter.description) return "Frontmatter 必须包含 description 字段。";
1070
+ if (parsed.frontmatter.description.length > limits.maxDescriptionLength) return `description 超过 ${limits.maxDescriptionLength} 个字符。`;
1071
+ if (exceedsContentLimit(content, limits.maxSkillContentChars) && !shrinksOverLimit(content, current, limits.maxSkillContentChars)) return `SKILL.md 内容超过 ${limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`;
1065
1072
  return null;
1066
1073
  }
1067
1074
  /**
@@ -1076,8 +1083,8 @@ function authoringFeedback(frontmatter) {
1076
1083
  const over60 = description.length > 60;
1077
1084
  const hasColon = description.includes(":");
1078
1085
  const lines = [];
1079
- lines.push(over60 ? `Description is ${description.length}/60 characters — exceeds the 60-char authoring bar (Hermes standard; the catalog truncates at the configured platform cap).` : `Description ${description.length}/60 characters — within the authoring bar.`);
1080
- if (hasColon) lines.push("Description contains a colon — wrap the whole value in double quotes.");
1086
+ lines.push(over60 ? `描述为 ${description.length}/60 个字符 —— 超过 60 字符的作者规范上限(Hermes 标准;技能目录会按平台配置的上限截断)。` : `描述为 ${description.length}/60 个字符 —— 在作者规范上限之内。`);
1087
+ if (hasColon) lines.push("描述里含冒号 —— 请用双引号把整个取值包起来。");
1081
1088
  return {
1082
1089
  descriptionChars: description.length,
1083
1090
  over60,
@@ -1157,6 +1164,213 @@ function bodyCost(content) {
1157
1164
  };
1158
1165
  }
1159
1166
  //#endregion
1167
+ //#region lib/types/skill-lint.js
1168
+ const msg = (parts) => parts.map((p) => typeof p === "string" ? p : String(p)).join("");
1169
+ /** One rule yields AT MOST one finding (contract §3). `body-oversize-2x` is the only rule whose
1170
+ * severity can escalate a verdict to needs-restructure. */
1171
+ const RULES = [
1172
+ {
1173
+ id: "unreadable-skill",
1174
+ severity: "error",
1175
+ run: (facts) => facts.readable && facts.dims.bodyChars !== null ? null : {
1176
+ severity: "error",
1177
+ rule: "unreadable-skill",
1178
+ message: msg([facts.skillName, ": SKILL.md could not be read — no health claim is possible"]),
1179
+ count: 1
1180
+ }
1181
+ },
1182
+ {
1183
+ id: "body-oversize-2x",
1184
+ severity: "error",
1185
+ run: (facts) => facts.overTwiceSoftLine ? {
1186
+ severity: "error",
1187
+ rule: "body-oversize-2x",
1188
+ message: msg([
1189
+ facts.skillName,
1190
+ ": body is at or past twice the soft line (",
1191
+ facts.softLineChars,
1192
+ " chars)"
1193
+ ]),
1194
+ count: 1
1195
+ } : null
1196
+ },
1197
+ {
1198
+ id: "body-oversize",
1199
+ severity: "warn",
1200
+ run: (facts) => !facts.overTwiceSoftLine && facts.overSoftLine ? {
1201
+ severity: "warn",
1202
+ rule: "body-oversize",
1203
+ message: msg([
1204
+ facts.skillName,
1205
+ ": body is at or above the soft line (",
1206
+ facts.softLineChars,
1207
+ " chars)"
1208
+ ]),
1209
+ count: 1
1210
+ } : null
1211
+ },
1212
+ {
1213
+ id: "stamp-density",
1214
+ severity: "warn",
1215
+ run: (facts) => {
1216
+ const density = facts.dims.stampDensityPerKb;
1217
+ const hits = facts.dims.stampHits;
1218
+ if (density === null || hits === null || density < facts.stampLimitPerKb) return null;
1219
+ return {
1220
+ severity: "warn",
1221
+ rule: "stamp-density",
1222
+ message: msg([
1223
+ facts.skillName,
1224
+ ": stamp density ",
1225
+ density.toFixed(1),
1226
+ "/KB (rc/sha/date lines — log-like content)"
1227
+ ]),
1228
+ count: hits
1229
+ };
1230
+ }
1231
+ },
1232
+ {
1233
+ id: "body-without-support",
1234
+ severity: "warn",
1235
+ run: (facts) => facts.dims.supportGroups === 0 && facts.dims.bodyChars !== null && facts.dims.bodyChars >= facts.softLineChars / 2 ? {
1236
+ severity: "warn",
1237
+ rule: "body-without-support",
1238
+ message: msg([
1239
+ facts.skillName,
1240
+ ": large body (",
1241
+ facts.dims.bodyChars,
1242
+ " chars) with NO support files"
1243
+ ]),
1244
+ count: 1
1245
+ } : null
1246
+ },
1247
+ {
1248
+ id: "references-sprawl",
1249
+ severity: "warn",
1250
+ run: (facts) => {
1251
+ const files = facts.dims.supportFiles;
1252
+ if (files === null || files < 60) return null;
1253
+ return {
1254
+ severity: "warn",
1255
+ rule: "references-sprawl",
1256
+ message: msg([
1257
+ facts.skillName,
1258
+ ": ",
1259
+ files,
1260
+ " support files"
1261
+ ]),
1262
+ count: files
1263
+ };
1264
+ }
1265
+ },
1266
+ {
1267
+ id: "write-ghost",
1268
+ severity: "warn",
1269
+ run: (facts) => {
1270
+ const patches = facts.dims.churnPatches;
1271
+ const reads = facts.dims.churnReads;
1272
+ if (patches === null || reads === null || patches < facts.churnMinPatches || reads > 0) return null;
1273
+ return {
1274
+ severity: "warn",
1275
+ rule: "write-ghost",
1276
+ message: msg([
1277
+ facts.skillName,
1278
+ ": ",
1279
+ patches,
1280
+ " patch(es), never read"
1281
+ ]),
1282
+ count: patches
1283
+ };
1284
+ }
1285
+ }
1286
+ ];
1287
+ function lintSkill(facts) {
1288
+ const out = [];
1289
+ for (const rule of RULES) {
1290
+ const hit = rule.run(facts);
1291
+ if (hit !== null) out.push(hit);
1292
+ }
1293
+ return out;
1294
+ }
1295
+ /** What a write introduced, what got worse, what got fixed. A rule whose count grew (one site to
1296
+ * nine) counts as worsened — comparing by rule name alone reported nothing. */
1297
+ function diffFindings(before, after) {
1298
+ const beforeByRule = new Map(before.map((f) => [f.rule, f]));
1299
+ const afterByRule = new Map(after.map((f) => [f.rule, f]));
1300
+ return {
1301
+ introduced: after.filter((f) => !beforeByRule.has(f.rule)),
1302
+ worsened: after.filter((f) => {
1303
+ const old = beforeByRule.get(f.rule);
1304
+ return old !== void 0 && f.count > old.count;
1305
+ }),
1306
+ resolved: before.filter((f) => !afterByRule.has(f.rule)).map((f) => f.rule)
1307
+ };
1308
+ }
1309
+ /** Support files = markdown files under references/ (contract §4: unit frozen, underscore-prefixed
1310
+ * excluded). Paths are supplied by the caller — this module never touches the filesystem. */
1311
+ function countReferenceFiles(paths) {
1312
+ return paths.filter((p) => {
1313
+ const n = p.replace(/\\/g, "/");
1314
+ if (!(n.includes("/references/") || n.startsWith("references/")) || !n.endsWith(".md")) return false;
1315
+ return !n.slice(n.lastIndexOf("/") + 1).startsWith("_");
1316
+ }).length;
1317
+ }
1318
+ //#endregion
1319
+ //#region lib/types/health-baseline.js
1320
+ const DEFAULT_ADMISSION = {
1321
+ minUserHitRate: .05,
1322
+ maxUserHitRate: .25
1323
+ };
1324
+ function buildHealthBaseline(skills) {
1325
+ const values = {};
1326
+ for (const rule of RULES) values[rule.id] = {
1327
+ platformHits: 0,
1328
+ userHits: 0,
1329
+ userHitRate: 0
1330
+ };
1331
+ let user = 0;
1332
+ let platform = 0;
1333
+ for (const skill of skills) {
1334
+ if (skill.corpus === "user") user += 1;
1335
+ else platform += 1;
1336
+ for (const finding of lintSkill(skill.facts)) {
1337
+ const row = values[finding.rule];
1338
+ if (row === void 0) continue;
1339
+ if (skill.corpus === "user") row.userHits += 1;
1340
+ else row.platformHits += 1;
1341
+ }
1342
+ }
1343
+ for (const row of Object.values(values)) row.userHitRate = user === 0 ? 0 : row.userHits / user;
1344
+ return {
1345
+ unit: "chars",
1346
+ scope: "references markdown file count",
1347
+ corpora: {
1348
+ user,
1349
+ platform
1350
+ },
1351
+ values
1352
+ };
1353
+ }
1354
+ /** The admission verdict for ONE rule id (contract §4): platform corpus must not trip it, and the
1355
+ * user-library hit rate must land inside the band. Every failure names the number it failed on. */
1356
+ function admissionVerdict(baseline, ruleId, thresholds = DEFAULT_ADMISSION) {
1357
+ const row = baseline.values[ruleId];
1358
+ const failures = [];
1359
+ if (row === void 0) return {
1360
+ admitted: false,
1361
+ failures: ["rule " + ruleId + " has no baseline row"]
1362
+ };
1363
+ if (baseline.corpora.platform === 0) failures.push("no platform-corpus skills measured — a vacuous 0-hit count admits any rule");
1364
+ else if (row.platformHits !== 0) failures.push("platform corpus trips " + ruleId + " " + String(row.platformHits) + " time(s) — a rule the shipped library violates is unsatisfiable");
1365
+ if (baseline.corpora.user === 0) failures.push("no user-library skills measured — a rate over an empty corpus proves nothing");
1366
+ else if (row.userHitRate < thresholds.minUserHitRate) failures.push(ruleId + " hits " + row.userHitRate.toFixed(3) + " of the user library, below " + String(thresholds.minUserHitRate) + " — too rare to be worth a rule");
1367
+ else if (row.userHitRate > thresholds.maxUserHitRate) failures.push(ruleId + " hits " + row.userHitRate.toFixed(3) + " of the user library, above " + String(thresholds.maxUserHitRate) + " — furniture, no signal");
1368
+ return {
1369
+ admitted: failures.length === 0,
1370
+ failures
1371
+ };
1372
+ }
1373
+ //#endregion
1160
1374
  //#region lib/types/guards.js
1161
1375
  /**
1162
1376
  * The family's plain-object predicate, in ONE place (v46 G7.1 / audit A8).
@@ -3102,33 +3316,33 @@ function allowRowCollisions(env = process.env) {
3102
3316
  * @module @lmzhen/dsh-evolution-core
3103
3317
  */
3104
3318
  const EVOLUTION_ERRORS = Object.freeze({
3105
- "e-305-this-invocation-carries-no": "E-305: this invocation carries no agent — `%a1%` needs a session-backed call (run it from a session in the GUI or the CLI).",
3106
- "e-306-this-deployment-stages-foreground": "E-306: this deployment stages foreground writes, but `/evolution %a1%` 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.",
3107
- "e-301-approval-service-not-mounted": "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.",
3108
- "e-301-approval-service-not-mounted-2": "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.",
3109
- "e-304-the-mounted-approval-service": "E-304: the mounted approval service predates the release capability — reject the record instead.",
3110
- "e-302-curator-service-not-mounted": "E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.",
3111
- "e-305-the-invocation-agent-exposes": "E-305: the invocation agent exposes neither `followup` nor `inject` — %a1% has no delivery channel.",
3112
- "e-306-this-deployment-stages-foreground-2": "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.",
3113
- "e-303-replay-service-not-mounted": "E-303: replay service not mounted. Next: mount the evolution-replay row (evolution-host/evolution-all) and run /evolution doctor.",
3114
- "e-307-the-settings-service-could": "E-307: the settings service could not report its sections (%a1%). /evolution params needs the user layer to tell an override from a deployment value; run /evolution doctor to see which services are mounted.",
3115
- "e-308-unknown-parameter-group-group": "E-308: unknown parameter group \"%a1%\" — known groups: %a2%.",
3116
- "e-311-no-settings-service-is": "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.",
3117
- "e-312-error-instanceof-Error-error": "E-312: %a1% — /evolution params lists the canonical ids.",
3118
- "e-313-unknown-parameter-id-evolution": "E-313: unknown parameter \"%a1%\" — /evolution params lists every registered id with its tier and user layer.",
3119
- "e-314-id-is-a-deployment": "E-314: \"%a1%\" is a deployment parameter (tier %a2%, owner %a3%) — it is written in cordis.yml on its plugin or policy row, not from a session. /evolution params shows who may write each row.",
3120
- "e-315-id-has-no-user": "E-315: \"%a1%\" has no user layer — owner %a2% publishes no settings namespace, so the value stays with the deployment.",
3121
- "e-307-the-settings-service-could-2": "E-307: the settings service could not report its sections (%a1%). /evolution policy set needs the current revision to write safely; run /evolution doctor to see which services are mounted.",
3122
- "e-316-namespace-namespace-is-not": "E-316: namespace \"%a1%\" is not registered — owner %a2% is not mounted in this composition, so its parameters cannot be written from here. /evolution params marks those rows 'unregistered'.",
3123
- "e-309-revision-conflict-you-sent": "E-309: revision conflict — you sent %a1%, the document stands at %a2%. Re-read with /evolution params --json and retry.",
3124
- "e-309-revision-conflict-message-Re": "E-309: revision conflict — %a1%. Re-read with /evolution params --json and retry.",
3125
- "e-310-the-settings-service-refused": "E-310: the settings service refused the write: %a1% — the owning plugin's rule stands (a cross-field pair, or a cap that may only be tightened). /evolution params shows the current value.",
3126
- "e-317-skill-write-not-confirmed": "E-317: skill \"%a1%\" was not %a2% — the confirmation prompt was declined or dismissed, so nothing was written. The prompt appears on every create and bare delete; repeat the call only if the operator asks for it.",
3127
- "e-318-skill-write-without-a-read": "E-318: skill \"%a1%\" was not read in this session, so this write is refused. Read it with the `skill` tool first (a read that failed does not count), then repeat the write.",
3128
- "e-320-the-profile-home-is-not": "E-320: the profile home is not reported by this host, so the legacy settings document cannot be located. /evolution migrate reads `<profile home>/settings.yaml.imported`; run it from a profile-launched session.",
3129
- "e-322-a-parameter-would-break-a": "E-322: %a1% must be at least %a2%, but this write sets it to %a3%. Nothing was written.",
3130
- "e-321-no-io-provider-is": "E-321: no evolution-io provider is mounted, so the legacy settings document cannot be read. Mount the evolution-io row (evolution-host/evolution-all) and retry.",
3131
- "e-319-skill-write-confirm-timed-out": "E-319: skill \"%a1%\" was not %a2% — the confirmation prompt went unanswered for %a3% second(s), so the write was cancelled. Nobody was there to decide: set `skillWriteConfirm` to `auto` to write without a prompt, keep `ask` to wait until someone answers, or raise `skillWriteConfirmTimeoutSeconds`."
3319
+ "e-305-this-invocation-carries-no": "E-305: 本次调用没有携带 agent —— `%a1%` 需要由会话支撑的调用(请在 GUI 或 CLI 的会话里运行)。",
3320
+ "e-306-this-deployment-stages-foreground": "E-306: 本部署会暂存前台写入,但 `/evolution %a1%` 无法通过技能运行器回放 —— 没有可暂存的内容。请在批准策略为 'never' 的会话里运行,或在 evolution-approval 行上设置 `stageForeground: false`,然后重试该命令。",
3321
+ "e-301-approval-service-not-mounted": "E-301: 批准服务未挂载:待批准写入无法列出或回放。挂载 evolution-approval 行(随 evolution-host/evolution-all 提供)后,运行 /evolution doctor 可查看已挂载的服务。",
3322
+ "e-301-approval-service-not-mounted-2": "E-301: 批准服务未挂载。下一步:evolution-approval 行随 evolution-host/evolution-all 一起提供 —— 运行 /evolution doctor 查看已挂载的服务。",
3323
+ "e-304-the-mounted-approval-service": "E-304: 已挂载的批准服务早于本能力发布 —— 请改为驳回该记录。",
3324
+ "e-302-curator-service-not-mounted": "E-302: 策展服务未挂载。下一步:挂载 evolution-curator 行(evolution-host/evolution-all),并运行 /evolution doctor。",
3325
+ "e-305-the-invocation-agent-exposes": "E-305: 调用 agent 既没有暴露 `followup` 也没有 `inject` —— %a1% 没有投递通道。",
3326
+ "e-306-this-deployment-stages-foreground-2": "E-306: 本部署会暂存前台写入,但本次调用没有 agent 会话 —— `/evolution restructure` 会暂存一条没有会话归属的记录。请在 GUI 或 CLI 的会话里运行,或在 `stageForeground: false` 行上设置,然后重试该命令。",
3327
+ "e-303-replay-service-not-mounted": "E-303: 回放服务未挂载。下一步:挂载 evolution-replay 行(evolution-host/evolution-all),并运行 /evolution doctor。",
3328
+ "e-307-the-settings-service-could": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution params 需要用户层来区分覆盖值与部署值;运行 /evolution doctor 查看已挂载的服务。",
3329
+ "e-308-unknown-parameter-group-group": "E-308: 未知的参数分组「%a1%」—— 已知分组:%a2%。",
3330
+ "e-311-no-settings-service-is": "E-311: 没有挂载设置服务,因此没有可写入的用户层。请挂载 settings 行(packages/settings/settings-file)后重试;本命令从不修改 cordis.yml —— 部署值属于部署。/evolution doctor 会列出已挂载的服务。",
3331
+ "e-312-error-instanceof-Error-error": "E-312: %a1% —— /evolution params 会列出规范 id。",
3332
+ "e-313-unknown-parameter-id-evolution": "E-313: 未知参数「%a1%」—— /evolution params 会列出每个已注册 id 及其层级与用户层。",
3333
+ "e-314-id-is-a-deployment": "E-314: 「%a1%」是部署参数(层级 %a2%,归属 %a3%)—— 它写在 cordis.yml 的插件行或策略行上,而不是从会话写入。/evolution params 会显示每一行由谁可写。",
3334
+ "e-315-id-has-no-user": "E-315: 「%a1%」没有用户层 —— 归属 %a2% 没有发布设置命名空间,因此该值留在部署层。",
3335
+ "e-307-the-settings-service-could-2": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution policy set 需要当前修订号才能安全写入;运行 /evolution doctor 查看已挂载的服务。",
3336
+ "e-316-namespace-namespace-is-not": "E-316: namespace \"%a1%\" 未注册 —— 归属 %a2% 在本组合中未挂载,因此无法从这里写入它的参数。 /evolution params 会把那些行标为 'unregistered'.",
3337
+ "e-309-revision-conflict-you-sent": "E-309: 修订冲突 —— 你提交的是 %a1%,文档当前为 %a2%。请用 /evolution params --json 重新读取后重试。",
3338
+ "e-309-revision-conflict-message-Re": "E-309: 修订冲突 —— %a1%。请用 /evolution params --json 重新读取后重试。",
3339
+ "e-310-the-settings-service-refused": "E-310: 设置服务拒绝了这次写入:%a1% —— 归属插件的规则优先(跨字段成对约束,或只能收紧的上限)。/evolution params 会显示当前值。",
3340
+ "e-317-skill-write-not-confirmed": "E-317: 技能 \"%a1%\" 未被%a2% —— 确认提示被拒绝或关闭,因此没有写入任何内容。每次创建与裸删除都会出现该提示;只有操作者要求时才重复调用。",
3341
+ "e-318-skill-write-without-a-read": "E-318: 本会话没有读取技能 \"%a1%\",因此拒绝这次写入。请先用 `skill` 工具读取它(失败的读取不算),然后重复该写入。",
3342
+ "e-320-the-profile-home-is-not": "E-320: 本宿主没有报告 profile home,因此无法定位旧设置文档。/evolution migrate 读取 `<profile home>/settings.yaml.imported`;请在由 profile 启动的会话里运行。",
3343
+ "e-322-a-parameter-would-break-a": "E-322: %a1% 至少应为 %a2%,但这次写入把它设成 %a3%。未写入任何内容。",
3344
+ "e-321-no-io-provider-is": "E-321: 没有挂载 evolution-io 提供方,因此无法读取旧设置文档。请挂载 evolution-io 行(evolution-host/evolution-all)后重试。",
3345
+ "e-319-skill-write-confirm-timed-out": "E-319: 技能 \"%a1%\" 未被%a2% —— 确认提示在 %a3% 秒内无人应答,因此该写入被取消。没有人在场决定:把 `skillWriteConfirm` 设为 `auto` 可免提示直接写入,保留 `ask` 则一直等到有人应答,或提高 `skillWriteConfirmTimeoutSeconds`。"
3132
3346
  });
3133
3347
  /**
3134
3348
  * Render one table entry, substituting its interpolation slots.
@@ -3821,7 +4035,7 @@ var MemoryStore = class {
3821
4035
  const chars = entries.join(ENTRY_DELIMITER).length;
3822
4036
  if (this.failureCount > this.maxFailures) return {
3823
4037
  ok: false,
3824
- message: `Memory consolidation failed ${this.failureCount} times this turn. Stop retrying memory calls and continue with the user's task.${previewEntries(entries)}`,
4038
+ message: `本轮记忆整理已失败 ${this.failureCount} 次。请停止重试记忆调用,继续处理用户的任务。${previewEntries(entries)}`,
3825
4039
  entries,
3826
4040
  chars,
3827
4041
  limit: this.limitFor(target)
@@ -3884,10 +4098,10 @@ var MemoryStore = class {
3884
4098
  const oversized = await this.oversizedFile(target);
3885
4099
  if (!oversized) return null;
3886
4100
  const backup = await this.backupFile(target);
3887
- const suffix = backup ? ` A backup was saved to ${basename(backup)}.` : "";
4101
+ const suffix = backup ? ` 已把备份保存到 ${basename(backup)}。` : "";
3888
4102
  return {
3889
4103
  ok: false,
3890
- message: `Memory file is ${oversized.size} bytes (limit ${oversized.limit * READ_GUARD_FACTOR}) — skipping read.${suffix} Fix the file manually, then retry.`,
4104
+ message: `记忆文件为 ${oversized.size} 字节(上限 ${oversized.limit * READ_GUARD_FACTOR})—— 跳过读取。${suffix} 请手动修复该文件后重试。`,
3891
4105
  entries: [],
3892
4106
  chars: 0,
3893
4107
  limit: this.limitFor(target)
@@ -3920,7 +4134,7 @@ var MemoryStore = class {
3920
4134
  const size = await this.io.size?.(fileFor(this.root, target));
3921
4135
  if (typeof size === "number" && size > SHRINK_LOAD_CEILING) return {
3922
4136
  ok: false,
3923
- message: `Memory file is ${size} bytes - too large to load even for a remove-only batch. Fix the file manually, then retry.`,
4137
+ message: `记忆文件为 ${size} 字节 —— 即使只做删除批处理也过大、无法加载。请手动修复该文件后重试。`,
3924
4138
  entries: [],
3925
4139
  chars: 0,
3926
4140
  limit: this.limitFor(target)
@@ -3934,7 +4148,7 @@ var MemoryStore = class {
3934
4148
  });
3935
4149
  return outcome ?? {
3936
4150
  ok: false,
3937
- message: "internal error: the memory transaction did not invoke the task; no write was performed",
4151
+ message: "内部错误:记忆写事务没有调用任务;未执行任何写入",
3938
4152
  entries: [],
3939
4153
  chars: 0,
3940
4154
  limit: this.limitFor(target)
@@ -3946,7 +4160,7 @@ var MemoryStore = class {
3946
4160
  async addChained(target, facts) {
3947
4161
  if (!facts.trim()) return {
3948
4162
  ok: false,
3949
- message: "Content cannot be empty.",
4163
+ message: "内容不能为空。",
3950
4164
  entries: [],
3951
4165
  chars: 0,
3952
4166
  limit: this.limitFor(target)
@@ -3963,7 +4177,7 @@ var MemoryStore = class {
3963
4177
  if (!content) return {
3964
4178
  result: {
3965
4179
  ok: false,
3966
- message: "Content cannot be empty.",
4180
+ message: "内容不能为空。",
3967
4181
  entries: [],
3968
4182
  chars: 0,
3969
4183
  limit: this.limitFor(target)
@@ -3990,7 +4204,7 @@ var MemoryStore = class {
3990
4204
  if (hasEntryDelimiter(prefixed)) return {
3991
4205
  result: {
3992
4206
  ok: false,
3993
- message: "Fact contains the entry delimiter (§) and would split into multiple entries; rewrite it as separate facts.",
4207
+ message: "该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。",
3994
4208
  entries: [],
3995
4209
  chars: 0,
3996
4210
  limit: this.limitFor(target)
@@ -4003,7 +4217,7 @@ var MemoryStore = class {
4003
4217
  return {
4004
4218
  result: {
4005
4219
  ok: true,
4006
- message: `Entry already exists (no duplicate added).${this.storageHint(target, entries.join(ENTRY_DELIMITER).length)}`,
4220
+ message: `该条目已存在(未重复添加)。${this.storageHint(target, entries.join(ENTRY_DELIMITER).length)}`,
4007
4221
  entries,
4008
4222
  chars: entries.join(ENTRY_DELIMITER).length,
4009
4223
  limit: this.limitFor(target)
@@ -4015,14 +4229,14 @@ var MemoryStore = class {
4015
4229
  const total = next.join(ENTRY_DELIMITER).length;
4016
4230
  const addLimit = this.limitFor(target);
4017
4231
  if (addLimit > 0 && total > addLimit) return {
4018
- result: this.failure(target, `Adding this entry would exceed the ${addLimit} char limit. Consolidate or remove stale entries, then retry.`, entries),
4232
+ result: this.failure(target, `添加该条目会超过 ${addLimit} 字符上限。请先合并或删除过期条目,再重试。`, entries),
4019
4233
  write: null
4020
4234
  };
4021
4235
  this.resetFailures();
4022
4236
  return {
4023
4237
  result: {
4024
4238
  ok: true,
4025
- message: `Entry added.${this.storageHint(target, total)}`,
4239
+ message: `已添加条目。${this.storageHint(target, total)}`,
4026
4240
  entries: next,
4027
4241
  chars: total,
4028
4242
  limit: this.limitFor(target)
@@ -4099,10 +4313,9 @@ var MemoryStore = class {
4099
4313
  if (shrinkOnly) return null;
4100
4314
  const limit = this.limitFor(target);
4101
4315
  const entries = normalizeEntries(raw);
4102
- const over = entries.filter((entry) => entry.length > limit).length;
4103
4316
  return {
4104
4317
  ok: false,
4105
- message: `${over} memory ${over === 1 ? "entry exceeds" : "entries exceed"} the configured ${target}CharLimit (${limit}); they were written under a higher limit. Raise the limit or remove entries — remove operations stay available.`,
4318
+ message: `${entries.filter((entry) => entry.length > limit).length} 条记忆超过了配置的 ${target}CharLimit(${limit});它们是在更高上限下写入的。请提高上限或删除条目 —— 删除类操作仍然可用。`,
4106
4319
  entries,
4107
4320
  chars: entries.join(ENTRY_DELIMITER).length,
4108
4321
  limit
@@ -4111,7 +4324,7 @@ var MemoryStore = class {
4111
4324
  const backup = await this.backupFile(target);
4112
4325
  return {
4113
4326
  ok: false,
4114
- message: `External drift detected in memory file.${backup ? ` A backup was saved to ${basename(backup)}.` : ""} Resolve the drift before retrying.`,
4327
+ message: `检测到记忆文件被外部改动。${backup ? ` 已把备份保存到 ${basename(backup)}。` : ""} 请先处理该漂移再重试。`,
4115
4328
  entries: [],
4116
4329
  chars: 0,
4117
4330
  limit: this.limitFor(target)
@@ -4123,7 +4336,7 @@ var MemoryStore = class {
4123
4336
  async applyBatchChained(target, operations) {
4124
4337
  if (operations.length === 0) return {
4125
4338
  ok: false,
4126
- message: "operations list is empty.",
4339
+ message: "operations 列表为空。",
4127
4340
  entries: [],
4128
4341
  chars: 0,
4129
4342
  limit: this.limitFor(target)
@@ -4148,7 +4361,7 @@ var MemoryStore = class {
4148
4361
  if (!body) return {
4149
4362
  result: {
4150
4363
  ok: false,
4151
- message: `Operation ${position} (add): facts is required. No operations were applied.${previewEntries(entries)}`,
4364
+ message: `第 ${position} 个操作(add):必须提供 facts。未应用任何操作。${previewEntries(entries)}`,
4152
4365
  entries,
4153
4366
  chars: entries.join(ENTRY_DELIMITER).length,
4154
4367
  limit: this.limitFor(target)
@@ -4159,7 +4372,7 @@ var MemoryStore = class {
4159
4372
  if (threat) return {
4160
4373
  result: {
4161
4374
  ok: false,
4162
- message: `Operation ${position}: ${threat}${previewEntries(entries)}`,
4375
+ message: `第 ${position} 个操作:${threat}${previewEntries(entries)}`,
4163
4376
  entries,
4164
4377
  chars: entries.join(ENTRY_DELIMITER).length,
4165
4378
  limit: this.limitFor(target)
@@ -4170,7 +4383,7 @@ var MemoryStore = class {
4170
4383
  if (hasEntryDelimiter(entryBody)) return {
4171
4384
  result: {
4172
4385
  ok: false,
4173
- message: `Operation ${position} (add): Fact contains the entry delimiter (§) and would split into multiple entries; rewrite it as separate facts.${previewEntries(entries)}`,
4386
+ message: `第 ${position} 个操作(add):该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。${previewEntries(entries)}`,
4174
4387
  entries,
4175
4388
  chars: entries.join(ENTRY_DELIMITER).length,
4176
4389
  limit: this.limitFor(target)
@@ -4184,7 +4397,7 @@ var MemoryStore = class {
4184
4397
  if (rawAction !== "remove" && rawAction !== "replace") return {
4185
4398
  result: {
4186
4399
  ok: false,
4187
- message: `Operation ${position}: unknown action "${String(rawAction)}" (expected add/remove/replace). No operations were applied.${previewEntries(entries)}`,
4400
+ message: `第 ${position} 个操作:未知动作「${String(rawAction)}」(应为 add/remove/replace)。未应用任何操作。${previewEntries(entries)}`,
4188
4401
  entries,
4189
4402
  chars: entries.join(ENTRY_DELIMITER).length,
4190
4403
  limit: this.limitFor(target)
@@ -4195,7 +4408,7 @@ var MemoryStore = class {
4195
4408
  if (!needle) return {
4196
4409
  result: {
4197
4410
  ok: false,
4198
- message: `Operation ${position} (${op.action}): old_text is required. No operations were applied.${previewEntries(entries)}`,
4411
+ message: `第 ${position} 个操作(${op.action}):必须提供 old_text。未应用任何操作。${previewEntries(entries)}`,
4199
4412
  entries,
4200
4413
  chars: entries.join(ENTRY_DELIMITER).length,
4201
4414
  limit: this.limitFor(target)
@@ -4207,13 +4420,13 @@ var MemoryStore = class {
4207
4420
  matchIndex
4208
4421
  })).filter(({ entry }) => entry.includes(needle));
4209
4422
  if (matches.length === 0) return {
4210
- result: this.failure(target, `Operation ${position}: no entry matching "${needle}" found. No operations were applied.`, entries),
4423
+ result: this.failure(target, `第 ${position} 个操作:找不到匹配 "${needle}" 的条目。未应用任何操作。`, entries),
4211
4424
  write: null
4212
4425
  };
4213
4426
  if (new Set(matches.map((m) => m.entry)).size > 1) return {
4214
4427
  result: {
4215
4428
  ok: false,
4216
- message: `Operation ${position}: "${needle}" matched multiple distinct entries. No operations were applied.${previewEntries(entries)}`,
4429
+ message: `第 ${position} 个操作:「${needle}」匹配到多条不同条目。未应用任何操作。${previewEntries(entries)}`,
4217
4430
  entries,
4218
4431
  chars: entries.join(ENTRY_DELIMITER).length,
4219
4432
  limit: this.limitFor(target)
@@ -4227,7 +4440,7 @@ var MemoryStore = class {
4227
4440
  if (!body) return {
4228
4441
  result: {
4229
4442
  ok: false,
4230
- message: `Operation ${position} (replace): facts is required.${previewEntries(entries)}`,
4443
+ message: `第 ${position} 个操作(replace):必须提供 facts。${previewEntries(entries)}`,
4231
4444
  entries,
4232
4445
  chars: entries.join(ENTRY_DELIMITER).length,
4233
4446
  limit: this.limitFor(target)
@@ -4238,7 +4451,7 @@ var MemoryStore = class {
4238
4451
  if (threat) return {
4239
4452
  result: {
4240
4453
  ok: false,
4241
- message: `Operation ${position}: ${threat}${previewEntries(entries)}`,
4454
+ message: `第 ${position} 个操作:${threat}${previewEntries(entries)}`,
4242
4455
  entries,
4243
4456
  chars: entries.join(ENTRY_DELIMITER).length,
4244
4457
  limit: this.limitFor(target)
@@ -4249,7 +4462,7 @@ var MemoryStore = class {
4249
4462
  if (hasEntryDelimiter(entryBody)) return {
4250
4463
  result: {
4251
4464
  ok: false,
4252
- message: `Operation ${position} (replace): Fact contains the entry delimiter (§) and would split into multiple entries; rewrite it as separate facts.${previewEntries(entries)}`,
4465
+ message: `第 ${position} 个操作(replace):该条目含有分隔符(§),会被拆成多条;请改写为多条独立的事实。${previewEntries(entries)}`,
4253
4466
  entries,
4254
4467
  chars: entries.join(ENTRY_DELIMITER).length,
4255
4468
  limit: this.limitFor(target)
@@ -4262,14 +4475,14 @@ var MemoryStore = class {
4262
4475
  const total = working.join(ENTRY_DELIMITER).length;
4263
4476
  const batchLimit = this.limitFor(target);
4264
4477
  if (batchLimit > 0 && total > batchLimit) return {
4265
- result: this.failure(target, `Batch result (${total} chars) exceeds the ${batchLimit} limit. Remove or shorten more entries in the same batch.`, entries),
4478
+ result: this.failure(target, `批处理结果(${total} 字符)超过 ${batchLimit} 上限。请在同一个批里再删除或缩短一些条目。`, entries),
4266
4479
  write: null
4267
4480
  };
4268
4481
  this.resetFailures();
4269
4482
  return {
4270
4483
  result: {
4271
4484
  ok: true,
4272
- message: `Applied ${operations.length} operation(s).${this.storageHint(target, total)}`,
4485
+ message: `已应用 ${operations.length} 个操作。${this.storageHint(target, total)}`,
4273
4486
  entries: working,
4274
4487
  chars: total,
4275
4488
  limit: this.limitFor(target)
@@ -5961,7 +6174,7 @@ function newRunRegistry(options) {
5961
6174
  const parsed = parseIndex(raw);
5962
6175
  if (!parsed.ok) return {
5963
6176
  ok: false,
5964
- note: "run index could not be read as an index (not JSON, or no runs array) — the previous run history is unknown (the reports are unaffected)"
6177
+ note: "运行索引读不出来(不是 JSON,或没有 runs 数组)—— 上一次的运行历史未知(报告不受影响)"
5965
6178
  };
5966
6179
  const wasRunning = new Set([...foreign.values()].filter((row) => row.state === "running").map((row) => row.id));
5967
6180
  const seen = /* @__PURE__ */ new Set();
@@ -6672,13 +6885,35 @@ const HEALTH_STAMP_RE = new RegExp(String.raw`\brc\.\d+\b|\b(?=[0-9a-f]{7,40}\b)
6672
6885
  * warned on a perfectly healthy body (audit 2026-08-31 X1).
6673
6886
  */
6674
6887
  const MIN_STAMP_BODY_CHARS = 2e3;
6888
+ /**
6889
+ * The findings for ONE body, computed by the engine that owns the judgement (G4).
6890
+ *
6891
+ * Deliberately a thin call into `assessStructureHealth`: a later consumer (the write receipt) needs
6892
+ * findings for a body it is about to store, and re-deriving the facts anywhere else would be a
6893
+ * SECOND judgement — the token-scale line, the /1024 stamp density and the soft/2 support threshold
6894
+ * all live in one place and must stay there.
6895
+ * @param skillName - the skill the body belongs to (used in messages).
6896
+ * @param content - the WHOLE SKILL.md text, frontmatter included (the engine's own convention).
6897
+ * @param supportGroups - non-empty support dirs; pass the real count, never a guess.
6898
+ * @param thresholds - the configured thresholds; defaults to the shipped ones.
6899
+ * @returns the findings, in rule order.
6900
+ */
6901
+ function lintBody(skillName, content, supportGroups, thresholds = DEFAULT_HEALTH_THRESHOLDS) {
6902
+ return assessStructureHealth({
6903
+ skillName,
6904
+ bodyChars: content.length,
6905
+ bodyText: content,
6906
+ supportGroups
6907
+ }, thresholds).findings;
6908
+ }
6675
6909
  function assessStructureHealth(snapshot, thresholds = DEFAULT_HEALTH_THRESHOLDS) {
6676
- const reasons = [];
6677
6910
  const dims = {
6678
6911
  bodyChars: snapshot.bodyChars,
6679
6912
  bodyTokens: Math.round(snapshot.bodyChars / UPSTREAM_LIMIT_CHARS_PER_TOKEN),
6680
6913
  stampDensityPerKb: null,
6914
+ stampHits: null,
6681
6915
  supportGroups: snapshot.supportGroups,
6916
+ supportFiles: snapshot.supportFiles ?? null,
6682
6917
  churnPatches: null,
6683
6918
  churnReads: null
6684
6919
  };
@@ -6688,25 +6923,30 @@ function assessStructureHealth(snapshot, thresholds = DEFAULT_HEALTH_THRESHOLDS)
6688
6923
  dims.bodyTokens = cost === null ? Math.round(snapshot.bodyChars / UPSTREAM_LIMIT_CHARS_PER_TOKEN) : cost.tokens;
6689
6924
  const overLine = cost === null || lineTokens === null ? snapshot.bodyChars >= thresholds.softBodyChars : cost.tokens >= lineTokens;
6690
6925
  const overTwice = cost === null || lineTokens === null ? snapshot.bodyChars >= thresholds.softBodyChars * 2 : cost.tokens >= lineTokens * 2;
6691
- const scale = cost === null || lineTokens === null ? `${snapshot.bodyChars} chars` : `${cost.tokens} tokens (${snapshot.bodyChars} chars)`;
6692
- const lineText = cost === null || lineTokens === null ? `${thresholds.softBodyChars} chars` : `${thresholds.softBodyChars} chars = ${lineTokens} tokens`;
6693
- if (overTwice) reasons.push(`body ${scale} is >= 2x the soft line (${lineText}) — consider splitting or offloading`);
6694
- else if (overLine) reasons.push(`body ${scale} at or above the soft line (${lineText})`);
6695
6926
  if (snapshot.bodyText && snapshot.bodyChars >= 2e3) {
6696
6927
  const kb = Math.max(1, snapshot.bodyChars / 1024);
6697
- dims.stampDensityPerKb = (snapshot.bodyText.match(HEALTH_STAMP_RE) ?? []).length / kb;
6698
- if (dims.stampDensityPerKb >= thresholds.stampDensityPerKb) reasons.push(`stamp density ${dims.stampDensityPerKb.toFixed(1)}/KB (rc/sha/date lines — log-like content in the body)`);
6928
+ const stamps = (snapshot.bodyText.match(HEALTH_STAMP_RE) ?? []).length;
6929
+ dims.stampDensityPerKb = stamps / kb;
6930
+ dims.stampHits = stamps;
6699
6931
  }
6700
- if (snapshot.supportGroups === 0 && snapshot.bodyChars >= thresholds.softBodyChars / 2) reasons.push(`large body (${snapshot.bodyChars} chars) with NO support files — session detail may belong in references/`);
6701
6932
  if (snapshot.patchCount !== void 0 && snapshot.readCount !== void 0) {
6702
6933
  dims.churnPatches = snapshot.patchCount;
6703
6934
  dims.churnReads = snapshot.readCount;
6704
- if (snapshot.patchCount >= thresholds.churnMinPatches && snapshot.readCount === 0) reasons.push(`patched ${snapshot.patchCount} times but never read (write-ghost — content may be dead)`);
6705
6935
  }
6936
+ const findings = lintSkill({
6937
+ skillName: snapshot.skillName,
6938
+ dims,
6939
+ softLineChars: thresholds.softBodyChars,
6940
+ overSoftLine: overLine,
6941
+ overTwiceSoftLine: overTwice,
6942
+ stampLimitPerKb: thresholds.stampDensityPerKb,
6943
+ churnMinPatches: thresholds.churnMinPatches,
6944
+ readable: true
6945
+ });
6706
6946
  return {
6707
- verdict: overTwice ? "needs-restructure" : reasons.length > 0 ? "warn" : "healthy",
6947
+ verdict: findings.some((f) => f.rule === "body-oversize-2x") ? "needs-restructure" : findings.length > 0 ? "warn" : "healthy",
6708
6948
  dims,
6709
- reasons
6949
+ findings
6710
6950
  };
6711
6951
  }
6712
6952
  //#endregion
@@ -7981,6 +8221,50 @@ function readingStats(full, shown) {
7981
8221
  truncated: shown.length < full.length
7982
8222
  };
7983
8223
  }
8224
+ /**
8225
+ * The H2 sections of a body, in document order — the ONE scan (G3).
8226
+ *
8227
+ * Both the restructure candidate list and the store anchor validation call this function, so a
8228
+ * candidate can only name a heading that exists EXACTLY ONCE and has content: the two rejections a
8229
+ * hand-written anchor used to hit ("no such heading", "ambiguous anchor") stop being reachable.
8230
+ * H2 is the section boundary (a section spans to the next H2 or EOF), matching the store.
8231
+ * @param body - the markdown after the frontmatter block.
8232
+ * @returns one row per H2 heading, with `count` filled in a second pass.
8233
+ */
8234
+ function h2Sections(body) {
8235
+ const lines = body.split(String.fromCharCode(10));
8236
+ const rows = [];
8237
+ for (let i = 0; i < lines.length; i += 1) {
8238
+ const match = /^#{2}\s+(.+?)\s*$/.exec(lines[i] ?? "");
8239
+ if (match === null) continue;
8240
+ const title = (match[1] ?? "").trim();
8241
+ let end = lines.length;
8242
+ for (let j = i + 1; j < lines.length; j += 1) if (/^#{2}\s/.test(lines[j] ?? "")) {
8243
+ end = j;
8244
+ break;
8245
+ }
8246
+ rows.push({
8247
+ title,
8248
+ start: i,
8249
+ end,
8250
+ count: 0
8251
+ });
8252
+ }
8253
+ for (const row of rows) row.count = rows.filter((other) => other.title === row.title).length;
8254
+ return rows;
8255
+ }
8256
+ /**
8257
+ * The sections a restructure proposal may name: heading unique, section non-empty. Derived from
8258
+ * `h2Sections`, i.e. from the SAME scan the store validates against.
8259
+ * @param body - the markdown after the frontmatter block.
8260
+ * @returns move-ready pairs in document order; empty when nothing qualifies.
8261
+ */
8262
+ function restructureCandidates(body) {
8263
+ return h2Sections(body).filter((section) => section.count === 1 && section.end > section.start + 1).map((section) => ({
8264
+ heading: section.title,
8265
+ toFile: "references/" + section.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fa5]+/g, "-").replace(/^-+|-+$/g, "") + ".md"
8266
+ }));
8267
+ }
7984
8268
  //#endregion
7985
8269
  //#region lib/types/drift-signals.js
7986
8270
  /**
@@ -8579,7 +8863,7 @@ function contentRetentionFeedback(input) {
8579
8863
  if (before === null || after === null) return null;
8580
8864
  if (before.length < 200) return null;
8581
8865
  if (after.length >= before.length * .5) return null;
8582
- return `Content kept ${Math.round(after.length / before.length * 100)}% of the previous body (${after.length} of ${before.length} characters); the replaced version is preserved in this skill's history.`;
8866
+ return `新内容保留了上一版正文的 ${Math.round(after.length / before.length * 100)}%(${after.length}/${before.length} 字符);被替换的版本已保留在该技能的历史里。`;
8583
8867
  }
8584
8868
  /**
8585
8869
  * Read one skill's versions, oldest first.
@@ -8947,7 +9231,7 @@ function anchorRefusal(name, verdict) {
8947
9231
  ok: false,
8948
9232
  stale: true,
8949
9233
  anchor: verdict,
8950
- message: verdict === "missing" ? `Skill "${name}" not found.` : `Skill "${name}" changed since it was read; the write was refused to avoid overwriting newer content.`
9234
+ message: verdict === "missing" ? `技能「${name}」不存在。` : `技能 "${name}" 在读取之后被改动过;为避免覆盖更新的内容,本次写入被拒绝。`
8951
9235
  };
8952
9236
  }
8953
9237
  /**
@@ -8970,7 +9254,7 @@ function anchorUnverifiable(name, filePath) {
8970
9254
  ok: false,
8971
9255
  stale: true,
8972
9256
  anchor: "drift",
8973
- message: filePath === null ? `Skill "${name}" could not be read to verify the staged content.` : `Support file "${filePath}" of "${name}" could not be read to verify the staged content.`
9257
+ message: filePath === null ? `无法读取技能 "${name}" 以校验暂存内容。` : `无法读取技能 "${name}" 的支撑文件 "${filePath}" 以校验暂存内容。`
8974
9258
  };
8975
9259
  }
8976
9260
  function anchorRefusalFile(name, filePath, verdict) {
@@ -8978,7 +9262,7 @@ function anchorRefusalFile(name, filePath, verdict) {
8978
9262
  ok: false,
8979
9263
  stale: true,
8980
9264
  anchor: verdict,
8981
- message: verdict === "missing" ? `File "${filePath}" not found in skill "${name}".` : `Support file "${filePath}" of "${name}" changed since it was read; the write was refused to avoid overwriting newer content.`
9265
+ message: verdict === "missing" ? `文件「${filePath}」不在技能「${name}」里。` : `技能 "${name}" 的支撑文件 "${filePath}" 在读取之后被改动过;为避免覆盖更新的内容,本次写入被拒绝。`
8982
9266
  };
8983
9267
  }
8984
9268
  /**
@@ -8996,7 +9280,7 @@ function anchorRefusalFile(name, filePath, verdict) {
8996
9280
  function unreadableTarget(label, error) {
8997
9281
  return {
8998
9282
  ok: false,
8999
- message: `Could not read "${label}": ${error?.code === "EISDIR" ? "the path is a DIRECTORY, not a file" : error instanceof Error ? error.message : String(error)}. Nothing was written.`
9283
+ message: `读不出「${label}」:${error?.code === "EISDIR" ? "the path is a DIRECTORY, not a file" : error instanceof Error ? error.message : String(error)}。没有写入任何内容。`
9000
9284
  };
9001
9285
  }
9002
9286
  /** Upper bound of moves per restructure proposal (validator and core agree). */
@@ -9180,15 +9464,15 @@ function validateRestructureTarget(filePath) {
9180
9464
  }
9181
9465
  function validateSupportPath(filePath) {
9182
9466
  const normalized = filePath.replace(/\\/g, "/");
9183
- if (normalized.includes("..")) return "Path traversal is not allowed.";
9467
+ if (normalized.includes("..")) return "不允许路径穿越。";
9184
9468
  const parts = normalized.split("/").filter(Boolean);
9185
9469
  if (parts.length === 0 || !SUPPORT_DIRS.includes(parts[0])) return `file_path must be under one of: ${SUPPORT_DIRS.join(", ")}.`;
9186
- if (parts.length < 2) return "Provide a file name, not just a directory.";
9470
+ if (parts.length < 2) return "请给出文件名,不能只给目录。";
9187
9471
  for (const part of parts.slice(1)) {
9188
- if (!SUPPORT_FILE_NAME_RE.test(part)) return `Unsupported file name "${part}" — use lowercase letters, digits, dots, hyphens, and underscores (leading letter or digit).`;
9189
- if (part.toLowerCase().endsWith(".corrupt") || part.toLowerCase().endsWith(".tmp")) return `Unsupported file name "${part}" — the .corrupt/.tmp suffixes are reserved for the state/IO protocols.`;
9472
+ if (!SUPPORT_FILE_NAME_RE.test(part)) return `不支持的文件名「${part}」:请只用小写字母、数字、点、连字符和下划线(首字符须为字母或数字)。`;
9473
+ if (part.toLowerCase().endsWith(".corrupt") || part.toLowerCase().endsWith(".tmp")) return `不支持的文件名「${part}」:.corrupt/.tmp 后缀由状态与 IO 协议保留。`;
9190
9474
  const stem = part.split(".")[0]?.toLowerCase() ?? "";
9191
- if (WIN32_RESERVED_DEVICE_NAMES.has(stem)) return `Unsupported file name "${part}" — a Windows reserved device name.`;
9475
+ if (WIN32_RESERVED_DEVICE_NAMES.has(stem)) return `不支持的文件名「${part}」:这是 Windows 保留设备名。`;
9192
9476
  }
9193
9477
  return null;
9194
9478
  }
@@ -9214,19 +9498,16 @@ function supportRefs(content) {
9214
9498
  }
9215
9499
  function planRestructureSections(body, moves) {
9216
9500
  const lines = body.split("\n");
9501
+ const sections = h2Sections(body);
9217
9502
  const spans = [];
9218
9503
  for (const move of moves) {
9219
9504
  const wanted = move.heading.trim();
9220
- const starts = [];
9221
- for (let i = 0; i < lines.length; i += 1) if ((/^#{2}\s+(.+?)\s*$/.exec(lines[i] ?? "")?.[1]?.trim() ?? "") === wanted) starts.push(i);
9222
- const [start] = starts;
9223
- if (start === void 0) return { error: `no "## ${wanted}" heading in the body` };
9224
- if (starts.length > 1) return { error: `heading "## ${wanted}" appears ${starts.length} times (ambiguous anchor)` };
9225
- let end = lines.length;
9226
- for (let i = start + 1; i < lines.length; i += 1) if (/^#{2}\s/.test(lines[i] ?? "")) {
9227
- end = i;
9228
- break;
9229
- }
9505
+ const matching = sections.filter((section) => section.title === wanted);
9506
+ if (matching.length === 0) return { error: `no "## ${wanted}" heading in the body` };
9507
+ if (matching.length > 1) return { error: `heading "## ${wanted}" appears ${matching.length} times (ambiguous anchor)` };
9508
+ const section = matching[0];
9509
+ if (section === void 0) return { error: `no "## ${wanted}" heading in the body` };
9510
+ const { start, end } = section;
9230
9511
  if (end === start + 1) return { error: `heading "## ${wanted}" has an empty section` };
9231
9512
  if (spans.some((span) => start === span.start)) return { error: `heading "## ${wanted}" is moved twice` };
9232
9513
  spans.push({
@@ -9337,7 +9618,7 @@ var SkillLibrary = class {
9337
9618
  const o = outcome;
9338
9619
  if (o === void 0 || typeof o !== "object" || !Object.prototype.hasOwnProperty.call(o, "write")) return {
9339
9620
  ok: false,
9340
- message: "internal error: the write transaction did not invoke the task; no write was performed"
9621
+ message: "内部错误:写事务没有调用任务;未执行任何写入"
9341
9622
  };
9342
9623
  if (o.ghostDir === true) await this.cleanupGhostDir(path);
9343
9624
  let result = o.result;
@@ -9357,7 +9638,7 @@ var SkillLibrary = class {
9357
9638
  if (o.write !== null && o.event) this.notifyMutation(o.event);
9358
9639
  return durabilityWarning === "" || !result.ok ? result : {
9359
9640
  ...result,
9360
- message: `${result.message} (warning: the write landed but the directory fsync failed — durability unconfirmed: ${durabilityWarning})`
9641
+ message: `${result.message} (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`
9361
9642
  };
9362
9643
  }
9363
9644
  /**
@@ -9520,7 +9801,7 @@ var SkillLibrary = class {
9520
9801
  */
9521
9802
  badName(name, opts = {}) {
9522
9803
  const normalized = name.trim();
9523
- if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return `Invalid skill name "${normalized}". Use lowercase letters, digits, and hyphens (<= ${this.limits.maxNameLength}).`;
9804
+ if (!SKILL_NAME_RE.test(normalized) || normalized.length > this.limits.maxNameLength) return `技能名「${normalized}」不合法:请只用小写字母、数字和连字符(不超过 ${this.limits.maxNameLength} 个)。`;
9524
9805
  if (!opts.allowReserved && WIN32_RESERVED_DEVICE_NAMES.has(normalized)) return `"${normalized}" is a Windows reserved device name and cannot be used as a skill name.`;
9525
9806
  return null;
9526
9807
  }
@@ -9573,6 +9854,32 @@ var SkillLibrary = class {
9573
9854
  return await this.io.exists(markerPath(dir, "pinned"));
9574
9855
  }
9575
9856
  /** Count non-empty support subdirectories (richness input for quality scoring). */
9857
+ /** G4 ⑤-d: the dangling targets THIS write introduced, as a receipt note (empty when none).
9858
+ *
9859
+ * Uses the SAME resolver as the drift signal, so a receipt and a drift row can never disagree;
9860
+ * an absent or unknown listing reports nothing rather than inventing a dead link.
9861
+ */
9862
+ async danglingNoteFor(skillName, before, after) {
9863
+ try {
9864
+ const listing = await this.listSupportFiles(skillName);
9865
+ if (!isPresent(listing)) return "";
9866
+ const files = listing.value;
9867
+ const owner = "SKILL.md";
9868
+ const seen = new Set(resolveCitations({
9869
+ content: before,
9870
+ file: owner,
9871
+ files
9872
+ }).dangling.map((ref) => ref.target ?? ref.raw));
9873
+ const introduced = resolveCitations({
9874
+ content: after,
9875
+ file: owner,
9876
+ files
9877
+ }).dangling.map((ref) => ref.target ?? ref.raw).filter((target) => !seen.has(target));
9878
+ return introduced.length > 0 ? " [dangling: " + introduced.join(",") + "]" : "";
9879
+ } catch {
9880
+ return "";
9881
+ }
9882
+ }
9576
9883
  async countSupportDirs(rawName) {
9577
9884
  const name = rawName.trim();
9578
9885
  if (this.badName(name) !== null) return 0;
@@ -9698,7 +10005,7 @@ var SkillLibrary = class {
9698
10005
  * mutation ledger that points at it. The history failure is not silent — a deployment whose
9699
10006
  * history stopped growing is a degradation worth seeing once (the ledger keeps hashes either
9700
10007
  * way). */
9701
- async audit(skillName, action, before, after, summary, path) {
10008
+ async audit(skillName, action, before, after, summary, path, origin) {
9702
10009
  const at = (/* @__PURE__ */ new Date()).toISOString();
9703
10010
  let versions = null;
9704
10011
  try {
@@ -9718,6 +10025,18 @@ var SkillLibrary = class {
9718
10025
  console.warn(`skill-store: content history not recorded for "${skillName}" (${error instanceof Error ? error.message : String(error)}); the mutation itself landed and the audit ledger keeps its hashes`);
9719
10026
  }
9720
10027
  }
10028
+ let healthNote = "";
10029
+ const danglingNote = before === null || after === null || before === after ? "" : await this.danglingNoteFor(skillName, before, after);
10030
+ if (before !== null && after !== null && before !== after) try {
10031
+ const groups = await this.countSupportDirs(skillName);
10032
+ const delta = diffFindings(lintBody(skillName, before, groups), lintBody(skillName, after, groups));
10033
+ const parts = [];
10034
+ if (delta.introduced.length > 0) parts.push("+" + delta.introduced.map((f) => f.rule).join(","));
10035
+ if (delta.worsened.length > 0) parts.push("^" + delta.worsened.map((f) => f.rule + " x" + String(f.count)).join(","));
10036
+ if (parts.length > 0) healthNote = " [health: " + parts.join(" ") + "]";
10037
+ } catch {
10038
+ healthNote = "";
10039
+ }
9721
10040
  try {
9722
10041
  await recordMutation(this.root, this.io, {
9723
10042
  skillName,
@@ -9726,8 +10045,9 @@ var SkillLibrary = class {
9726
10045
  ...after === null ? {} : { afterHash: contentHash(after) },
9727
10046
  ...versions?.beforeVersion === void 0 ? {} : { beforeVersion: versions.beforeVersion },
9728
10047
  ...versions?.afterVersion === void 0 ? {} : { afterVersion: versions.afterVersion },
9729
- summary,
9730
- at
10048
+ summary: summary + healthNote + danglingNote,
10049
+ at,
10050
+ ...origin === void 0 ? {} : { origin }
9731
10051
  });
9732
10052
  } catch {}
9733
10053
  }
@@ -9779,7 +10099,7 @@ var SkillLibrary = class {
9779
10099
  };
9780
10100
  if (origin === "background_review") return {
9781
10101
  ok: false,
9782
- message: "Only the foreground (user or the main agent) may pin or unpin skills."
10102
+ message: "只有前台(用户或主智能体)可以固定或取消固定技能。"
9783
10103
  };
9784
10104
  let durabilityWarning = "";
9785
10105
  const dir = this.dirOf(normalized);
@@ -9787,17 +10107,17 @@ var SkillLibrary = class {
9787
10107
  const existing = await this.io.exists(marker);
9788
10108
  if (pinned && existing) return {
9789
10109
  ok: true,
9790
- message: `Skill "${normalized}" is already pinned.`,
10110
+ message: `技能「${normalized}」已被固定。`,
9791
10111
  path: dir
9792
10112
  };
9793
10113
  if (!pinned && !existing) return {
9794
10114
  ok: true,
9795
- message: `Skill "${normalized}" is not pinned; nothing to do.`,
10115
+ message: `技能「${normalized}」未被固定,无需处理。`,
9796
10116
  path: dir
9797
10117
  };
9798
10118
  if (!await this.io.exists(join(dir, "SKILL.md"))) return {
9799
10119
  ok: false,
9800
- message: `Skill "${normalized}" not found.`
10120
+ message: `技能「${normalized}」不存在。`
9801
10121
  };
9802
10122
  if (pinned) {
9803
10123
  try {
@@ -9811,14 +10131,14 @@ var SkillLibrary = class {
9811
10131
  await this.cleanupGhostDir(join(dir, "SKILL.md"));
9812
10132
  return {
9813
10133
  ok: false,
9814
- message: `Skill "${normalized}" was archived concurrently while pinning; the partial marker was removed — retry after the mover settles.`
10134
+ message: `固定过程中技能「${normalized}」被并发归档;残留标记已移除:等搬运完成后重试。`
9815
10135
  };
9816
10136
  }
9817
10137
  } else await this.io.remove(marker);
9818
10138
  await this.audit(normalized, pinned ? "pin" : "unpin", null, null, pinned ? "pinned" : "unpinned");
9819
10139
  return {
9820
10140
  ok: true,
9821
- message: `${pinned ? `Skill "${normalized}" pinned: protected from deletion, background review, and the lifecycle.` : `Skill "${normalized}" unpinned.`}${durabilityWarning === "" ? "" : ` (warning: the write landed but the directory fsync failed — durability unconfirmed: ${durabilityWarning})`}`,
10141
+ message: `${pinned ? `技能 "${normalized}" 已固定:免受删除、后台复核与生命周期影响。` : `技能 "${normalized}" 已取消固定。`}${durabilityWarning === "" ? "" : ` (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`}`,
9822
10142
  path: dir
9823
10143
  };
9824
10144
  }
@@ -9840,7 +10160,7 @@ var SkillLibrary = class {
9840
10160
  const norm = normalizeFrontmatter(content);
9841
10161
  if (norm.issues.length > 0) return {
9842
10162
  ok: false,
9843
- message: `Skill "${normalized}" frontmatter cannot be auto-fixed: ${norm.issues[0]} — wrap the value in double quotes and retry.`
10163
+ message: `技能「${normalized}」的 frontmatter 无法自动修复:${norm.issues[0]}:请用双引号包住取值后重试。`
9844
10164
  };
9845
10165
  const finalContent = norm.changed ? norm.content : content;
9846
10166
  if (norm.changed) {
@@ -9858,7 +10178,7 @@ var SkillLibrary = class {
9858
10178
  const dir = this.dirOf(normalized);
9859
10179
  if (await this.io.exists(join(dir, "SKILL.md"))) return {
9860
10180
  ok: false,
9861
- message: `Skill "${normalized}" already exists.`
10181
+ message: `技能「${normalized}」已存在。`
9862
10182
  };
9863
10183
  let rootEntries;
9864
10184
  try {
@@ -9867,17 +10187,17 @@ var SkillLibrary = class {
9867
10187
  const cause = error instanceof Error ? error.message : String(error);
9868
10188
  return {
9869
10189
  ok: false,
9870
- message: `Skill "${normalized}" could not be checked for a case-variant collision: listing ${this.root} failed (${cause}). Refusing the create — a second directory differing only in case cannot be undone across platforms.`
10190
+ message: `无法检查技能「${normalized}」是否与大小写变体冲突:列出 ${this.root} 失败(${cause})。拒绝创建 —— 仅大小写不同的第二个目录在跨平台时无法撤销。`
9871
10191
  };
9872
10192
  }
9873
10193
  for (const entry of rootEntries) if (typeof entry === "string" && entry !== normalized && entry.toLowerCase() === normalized.toLowerCase()) return {
9874
10194
  ok: false,
9875
- message: `Skill "${normalized}" collides with the existing case-variant directory "${entry}" (skill names are lowercase-only); rename one of them.`
10195
+ message: `技能「${normalized}」与已存在的大小写变体目录「${entry}」冲突(技能名只允许小写):请重命名其中一个。`
9876
10196
  };
9877
10197
  const protection = await this.writeProtection(normalized, origin);
9878
10198
  if (protection) return {
9879
10199
  ok: false,
9880
- message: `Skill "${normalized}" is protected (${protection}).`
10200
+ message: `技能「${normalized}」受保护(${protection})。`
9881
10201
  };
9882
10202
  const onDisk = finalContent.trimEnd() + "\n";
9883
10203
  const createPath = join(dir, "SKILL.md");
@@ -9912,18 +10232,18 @@ var SkillLibrary = class {
9912
10232
  }
9913
10233
  if (!taskRan) return {
9914
10234
  ok: false,
9915
- message: "internal error: the create transaction did not invoke the task; no file was written"
10235
+ message: "内部错误:创建事务没有调用任务;未写入任何文件"
9916
10236
  };
9917
10237
  if (existsAtCommit) return {
9918
10238
  ok: false,
9919
- message: `Skill "${normalized}" already exists.`
10239
+ message: `技能「${normalized}」已存在。`
9920
10240
  };
9921
10241
  let markerWarning = "";
9922
10242
  if (origin !== "foreground") {
9923
10243
  try {
9924
10244
  await this.io.writeText(markerPath(dir, "hermes-managed"), "");
9925
10245
  } catch (error) {
9926
- if (isCommittedOnly(error)) markerWarning = `the hermes-managed marker landed but its directory fsync failed — durability unconfirmed: ${error instanceof Error ? error.message : String(error)}`;
10246
+ if (isCommittedOnly(error)) markerWarning = `hermes-managed 标记已落盘,但其目录 fsync 失败 —— 持久性未确认:${error instanceof Error ? error.message : String(error)}`;
9927
10247
  else markerWarning = `hermes-managed marker write failed (${error instanceof Error ? error.message : String(error)}); the skill landed but is NOT lifecycle-managed — recreate or pin it manually`;
9928
10248
  }
9929
10249
  if (!await this.io.exists(createPath)) {
@@ -9931,7 +10251,7 @@ var SkillLibrary = class {
9931
10251
  await this.cleanupGhostDir(createPath);
9932
10252
  return {
9933
10253
  ok: false,
9934
- message: `Skill "${normalized}" was archived concurrently while being created; the partial marker was removed — retry once the mover settles.`
10254
+ message: `创建过程中技能「${normalized}」被并发归档;残留标记已移除:等搬运完成后重试一次。`
9935
10255
  };
9936
10256
  }
9937
10257
  }
@@ -9941,10 +10261,10 @@ var SkillLibrary = class {
9941
10261
  name: normalized,
9942
10262
  skillDir: dir
9943
10263
  });
9944
- const warnings = [createDurabilityWarning !== "" ? `the write landed but the directory fsync failed — durability unconfirmed: ${createDurabilityWarning}` : "", markerWarning].filter((warning) => warning !== "");
10264
+ const warnings = [createDurabilityWarning !== "" ? `写入已落盘,但目录 fsync 失败 —— 持久性未确认:${createDurabilityWarning}` : "", markerWarning].filter((warning) => warning !== "");
9945
10265
  return {
9946
10266
  ok: true,
9947
- message: `Skill "${normalized}" created.${warnings.length === 0 ? "" : ` (warning: ${warnings.join("; ")})`}`,
10267
+ message: `已创建技能「${normalized}」。${warnings.length === 0 ? "" : ` (警告:${warnings.join(";")})`}`,
9948
10268
  path: dir,
9949
10269
  ...norm.changed ? { normalizedFrontmatterFields: norm.fields } : {}
9950
10270
  };
@@ -9964,7 +10284,7 @@ var SkillLibrary = class {
9964
10284
  const protection = await this.writeProtection(name, origin);
9965
10285
  if (protection) return {
9966
10286
  ok: false,
9967
- message: `Skill "${name}" is protected (${protection}).`
10287
+ message: `技能「${name}」受保护(${protection})。`
9968
10288
  };
9969
10289
  const limitProbe = await probeText(this.io, path);
9970
10290
  if (isUnknown(limitProbe)) console.warn(`skill-store: could not re-read ${path} for the shrink exemption (${limitProbe.reason}); validating without it — an over-limit repair is refused, not accepted`);
@@ -9977,7 +10297,7 @@ var SkillLibrary = class {
9977
10297
  const norm = normalizeFrontmatter(content);
9978
10298
  if (norm.issues.length > 0) return {
9979
10299
  ok: false,
9980
- message: `Skill "${name}" frontmatter cannot be auto-fixed: ${norm.issues[0]} — wrap the value in double quotes and retry.`
10300
+ message: `技能「${name}」的 frontmatter 无法自动修复:${norm.issues[0]}:请用双引号包住取值后重试。`
9981
10301
  };
9982
10302
  const finalContent = norm.changed ? norm.content : content;
9983
10303
  if (norm.changed) {
@@ -10001,14 +10321,14 @@ var SkillLibrary = class {
10001
10321
  if (current === null) return {
10002
10322
  result: {
10003
10323
  ok: false,
10004
- message: `Skill "${name}" not found.`
10324
+ message: `技能「${name}」不存在。`
10005
10325
  },
10006
10326
  write: null
10007
10327
  };
10008
10328
  if (finalContent.trimEnd() === current.trimEnd()) return {
10009
10329
  result: {
10010
10330
  ok: true,
10011
- message: `Skill "${name}" unchanged: the supplied content already matches the current file; nothing written.`,
10331
+ message: `技能「${name}」未变更:提供的内容与当前文件一致,未写入。`,
10012
10332
  noop: true,
10013
10333
  path: dir
10014
10334
  },
@@ -10018,7 +10338,7 @@ var SkillLibrary = class {
10018
10338
  return {
10019
10339
  result: {
10020
10340
  ok: true,
10021
- message: `Skill "${name}" updated.`,
10341
+ message: `技能「${name}」已更新。`,
10022
10342
  path: dir,
10023
10343
  ...norm.changed ? { normalizedFrontmatterFields: norm.fields } : {}
10024
10344
  },
@@ -10052,12 +10372,12 @@ var SkillLibrary = class {
10052
10372
  const skillMd = join(dir, "SKILL.md");
10053
10373
  if (!await this.io.exists(skillMd)) return {
10054
10374
  ok: false,
10055
- message: `Skill "${name}" not found.`
10375
+ message: `技能「${name}」不存在。`
10056
10376
  };
10057
10377
  const protection = await this.writeProtection(name, origin);
10058
10378
  if (protection) return {
10059
10379
  ok: false,
10060
- message: `Skill "${name}" is protected (${protection}).`
10380
+ message: `技能「${name}」受保护(${protection})。`
10061
10381
  };
10062
10382
  let target = skillMd;
10063
10383
  let patchLabel = "SKILL.md";
@@ -10075,7 +10395,7 @@ var SkillLibrary = class {
10075
10395
  if (md === null) return {
10076
10396
  result: {
10077
10397
  ok: false,
10078
- message: `File not found: ${patchLabel}`
10398
+ message: `文件不存在:${patchLabel}`
10079
10399
  },
10080
10400
  write: null
10081
10401
  };
@@ -10097,7 +10417,7 @@ var SkillLibrary = class {
10097
10417
  if (patched === null) return {
10098
10418
  result: {
10099
10419
  ok: false,
10100
- message: `Could not find old_string in "${name}/${patchLabel}" (or the replaceAll fuzzy budget was exceeded). Use update for a full rewrite.`
10420
+ message: `在「${name}/${patchLabel}」里找不到 old_string(或 replaceAll 的模糊匹配预算已用尽)。整篇重写请改用 update。`
10101
10421
  },
10102
10422
  write: null
10103
10423
  };
@@ -10110,7 +10430,7 @@ var SkillLibrary = class {
10110
10430
  if (validation) return {
10111
10431
  result: {
10112
10432
  ok: false,
10113
- message: `Patch rejected: ${validation}`
10433
+ message: `补丁被拒绝:${validation}`
10114
10434
  },
10115
10435
  write: null
10116
10436
  };
@@ -10118,7 +10438,7 @@ var SkillLibrary = class {
10118
10438
  if (norm.issues.length > 0) return {
10119
10439
  result: {
10120
10440
  ok: false,
10121
- message: `Patch rejected: frontmatter cannot be auto-fixed (${norm.issues[0]}).`
10441
+ message: `补丁被拒绝:frontmatter 无法自动修复(${norm.issues[0]})。`
10122
10442
  },
10123
10443
  write: null
10124
10444
  };
@@ -10129,7 +10449,7 @@ var SkillLibrary = class {
10129
10449
  if (revalidated) return {
10130
10450
  result: {
10131
10451
  ok: false,
10132
- message: `Patch rejected: ${revalidated}`
10452
+ message: `补丁被拒绝:${revalidated}`
10133
10453
  },
10134
10454
  write: null
10135
10455
  };
@@ -10138,7 +10458,7 @@ var SkillLibrary = class {
10138
10458
  if (Buffer.byteLength(writeContent, "utf8") > this.limits.maxSkillFileBytes && target !== skillMd) return {
10139
10459
  result: {
10140
10460
  ok: false,
10141
- message: `Patched file exceeds ${this.limits.maxSkillFileBytes} bytes.`
10461
+ message: `修补后的文件超过 ${this.limits.maxSkillFileBytes} 字节。`
10142
10462
  },
10143
10463
  write: null
10144
10464
  };
@@ -10147,7 +10467,7 @@ var SkillLibrary = class {
10147
10467
  if (overLimit && target === skillMd && !repairing) return {
10148
10468
  result: {
10149
10469
  ok: false,
10150
- message: `Patched content exceeds ${this.limits.maxSkillContentChars} characters. ${CONTENT_SPLIT_HINT}`
10470
+ message: `修补后的内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
10151
10471
  },
10152
10472
  write: null
10153
10473
  };
@@ -10156,7 +10476,7 @@ var SkillLibrary = class {
10156
10476
  if (supportOverCap && this.supportFileCharPolicy() === "enforce") return {
10157
10477
  result: {
10158
10478
  ok: false,
10159
- message: `Patched file content exceeds ${this.limits.maxSkillContentChars} characters. ${CONTENT_SPLIT_HINT}`
10479
+ message: `修补后的文件内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
10160
10480
  },
10161
10481
  write: null
10162
10482
  };
@@ -10171,7 +10491,7 @@ var SkillLibrary = class {
10171
10491
  if (writeContent.trimEnd() === md.trimEnd()) return {
10172
10492
  result: {
10173
10493
  ok: true,
10174
- message: `Skill "${name}" unchanged: old_string already equals the replacement (${patchLabel}); nothing written.`,
10494
+ message: `技能「${name}」未变更:old_string 与替换内容相同(${patchLabel}),未写入。`,
10175
10495
  noop: true,
10176
10496
  path: dir
10177
10497
  },
@@ -10181,7 +10501,7 @@ var SkillLibrary = class {
10181
10501
  return {
10182
10502
  result: {
10183
10503
  ok: true,
10184
- message: `Skill "${name}" patched (${patchLabel}).${patchNote}${capAdvisory}`,
10504
+ message: `技能「${name}」已修补(${patchLabel})。${patchNote}${capAdvisory}`,
10185
10505
  path: dir,
10186
10506
  ...normalizedFields ? { normalizedFrontmatterFields: normalizedFields } : {}
10187
10507
  },
@@ -10226,9 +10546,9 @@ var SkillLibrary = class {
10226
10546
  const reason = error instanceof Error ? error.message : String(error);
10227
10547
  try {
10228
10548
  await this.io.remove(dest);
10229
- return `the copy succeeded but the source could not be removed (${reason}); the copied tree was rolled back`;
10549
+ return `复制成功,但源目录删不掉(${reason});已回滚复制出的目录树`;
10230
10550
  } catch {
10231
- return `the copy succeeded but the source could not be removed (${reason}) and the copied tree could not be rolled back — the tree now exists in BOTH locations; clean up manually`;
10551
+ return `复制成功,但源目录删不掉(${reason}),且复制出的目录树也无法回滚 —— 该目录树现在同时存在于两处;请手动清理`;
10232
10552
  }
10233
10553
  }
10234
10554
  }
@@ -10271,17 +10591,17 @@ var SkillLibrary = class {
10271
10591
  const md = await this.io.readText(join(dir, "SKILL.md"));
10272
10592
  if (md === null) return {
10273
10593
  ok: false,
10274
- message: `Skill "${name}" not found.`
10594
+ message: `技能「${name}」不存在。`
10275
10595
  };
10276
10596
  const protection = await this.deleteProtection(name, options);
10277
10597
  if (protection) return {
10278
10598
  ok: false,
10279
- message: protection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `Skill "${name}" is protected (${protection}).`
10599
+ message: protection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `技能「${name}」受保护(${protection})。`
10280
10600
  };
10281
10601
  if (options.absorbedInto) {
10282
10602
  if (options.absorbedInto.trim() === name) return {
10283
10603
  ok: false,
10284
- message: "absorbed_into cannot be the skill being archived (cannot absorb into itself)."
10604
+ message: "absorbed_into 不能是正在归档的技能本身(不能吸收到自己)。"
10285
10605
  };
10286
10606
  const intoBad = this.badName(options.absorbedInto.trim());
10287
10607
  if (intoBad) return {
@@ -10303,22 +10623,22 @@ var SkillLibrary = class {
10303
10623
  if (this.io.isSymlink) {
10304
10624
  if (await this.io.isSymlink(dir) === true) return {
10305
10625
  ok: false,
10306
- message: `Skill "${name}" is a symlink; refusing to archive it.`
10626
+ message: `技能「${name}」是符号链接;拒绝归档。`
10307
10627
  };
10308
10628
  }
10309
10629
  if (await this.supportDirsUnlistable(dir)) return {
10310
10630
  ok: false,
10311
- message: `Skill "${name}" could not be checked for writers (a support directory could not be listed); retry archiving.`
10631
+ message: `无法检查技能「${name}」是否有写入方(有一个支撑目录列不出来):请重试归档。`
10312
10632
  };
10313
10633
  const commitProtection = await this.deleteProtection(name, options);
10314
10634
  if (commitProtection) return {
10315
10635
  ok: false,
10316
- message: commitProtection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `Skill "${name}" is protected (${commitProtection}).`
10636
+ message: commitProtection === "pinned" ? `Skill "${name}" is pinned and cannot be archived. Remove the \`.pinned\` marker in its directory, then retry.` : `技能「${name}」受保护(${commitProtection})。`
10317
10637
  };
10318
10638
  const moveFailure = await this.moveDir(dir, dest);
10319
10639
  if (moveFailure !== void 0) return {
10320
10640
  ok: false,
10321
- message: `Skill "${name}" archive failed: ${moveFailure}.`
10641
+ message: `技能「${name}」归档失败:${moveFailure}。`
10322
10642
  };
10323
10643
  const reason = options.reason ?? (options.absorbedInto ? `Consolidated into ${options.absorbedInto}` : "Archived by self-evolution curator");
10324
10644
  try {
@@ -10335,7 +10655,7 @@ var SkillLibrary = class {
10335
10655
  });
10336
10656
  return {
10337
10657
  ok: true,
10338
- message: `Skill "${name}" archived to .archive.`,
10658
+ message: `技能「${name}」已归档到 .archive。`,
10339
10659
  path: dest
10340
10660
  };
10341
10661
  }
@@ -10433,7 +10753,7 @@ var SkillLibrary = class {
10433
10753
  const mode = options.mode ?? "append";
10434
10754
  if (normalizedSources.length === 0) return {
10435
10755
  ok: false,
10436
- message: "Consolidation requires at least one distinct source skill."
10756
+ message: "合并至少需要一个互不相同的来源技能。"
10437
10757
  };
10438
10758
  for (const name of [targetName, ...normalizedSources]) {
10439
10759
  const bad = this.badName(name);
@@ -10446,7 +10766,7 @@ var SkillLibrary = class {
10446
10766
  const targetProtection = await this.writeProtection(targetName, origin);
10447
10767
  if (targetProtection) return {
10448
10768
  ok: false,
10449
- message: `Skill "${targetName}" is protected (${targetProtection}).`
10769
+ message: `技能「${targetName}」受保护(${targetProtection})。`
10450
10770
  };
10451
10771
  return await this.serial(async () => {
10452
10772
  const referenceWrites = [];
@@ -10458,17 +10778,17 @@ var SkillLibrary = class {
10458
10778
  const protection = await this.deleteProtection(source);
10459
10779
  if (protection) return {
10460
10780
  ok: false,
10461
- message: `Skill "${source}" is protected (${protection}).`
10781
+ message: `技能「${source}」受保护(${protection})。`
10462
10782
  };
10463
10783
  const sourceMd = await this.io.readText(join(this.dirOf(source), "SKILL.md"));
10464
10784
  if (!sourceMd) return {
10465
10785
  ok: false,
10466
- message: `Skill "${source}" not found.`
10786
+ message: `技能「${source}」不存在。`
10467
10787
  };
10468
10788
  const parsed = parseFrontmatter(sourceMd);
10469
10789
  if (!parsed) return {
10470
10790
  ok: false,
10471
- message: `Skill "${source}" has no valid frontmatter; refusing to merge.`
10791
+ message: `技能「${source}」没有有效的 frontmatter;拒绝合并。`
10472
10792
  };
10473
10793
  const carriesSupport = await this.countSupportDirs(source) > 0;
10474
10794
  const refs = supportRefs(parsed.body);
@@ -10477,7 +10797,7 @@ var SkillLibrary = class {
10477
10797
  const decision = await this.planSourceRehoming(source, targetName, parsed.body, rehomed.map((file) => file.to));
10478
10798
  if (decision.kind === "blocked") return {
10479
10799
  ok: false,
10480
- message: `Consolidation rejected: ${carriesSupport ? `source "${source}" carries support files` : `source "${source}" body references support files (${refs.join(", ")})`} that would be left behind — use mode:'reference' or archive the whole package instead.${decision.note}`
10800
+ message: `合并被拒绝:${carriesSupport ? `source "${source}" carries support files` : `source "${source}" body references support files (${refs.join(", ")})`} 会被落下。请改用 mode:'reference',或直接归档整个包。${decision.note}`
10481
10801
  };
10482
10802
  rehomed.push(...decision.files);
10483
10803
  appendedBody = decision.rewritten;
@@ -10489,17 +10809,17 @@ var SkillLibrary = class {
10489
10809
  const protection = await this.deleteProtection(source);
10490
10810
  if (protection) return {
10491
10811
  ok: false,
10492
- message: `Skill "${source}" is protected (${protection}).`
10812
+ message: `技能「${source}」受保护(${protection})。`
10493
10813
  };
10494
10814
  const sourceMd = await this.io.readText(join(this.dirOf(source), "SKILL.md"));
10495
10815
  if (!sourceMd) return {
10496
10816
  ok: false,
10497
- message: `Skill "${source}" not found.`
10817
+ message: `技能「${source}」不存在。`
10498
10818
  };
10499
10819
  const parsed = parseFrontmatter(sourceMd);
10500
10820
  if (!parsed) return {
10501
10821
  ok: false,
10502
- message: `Skill "${source}" has no valid frontmatter; refusing to demote.`
10822
+ message: `技能「${source}」没有有效的 frontmatter;拒绝降级。`
10503
10823
  };
10504
10824
  const refs = supportRefs(parsed.body);
10505
10825
  let demotedBody = parsed.body;
@@ -10507,7 +10827,7 @@ var SkillLibrary = class {
10507
10827
  const decision = await this.planSourceRehoming(source, targetName, parsed.body, rehomed.map((file) => file.to));
10508
10828
  if (decision.kind === "blocked") return {
10509
10829
  ok: false,
10510
- message: `Consolidation rejected: source "${source}" body references support files (${refs.join(", ")}) that would be left behind — archive the whole package instead.${decision.note}`
10830
+ message: `合并被拒绝:来源技能「${source}」的正文引用了会被落下的支撑文件(${refs.join(", ")}):请改为归档整个包。${decision.note}`
10511
10831
  };
10512
10832
  rehomed.push(...decision.files);
10513
10833
  demotedBody = decision.rewritten;
@@ -10524,7 +10844,7 @@ var SkillLibrary = class {
10524
10844
  try {
10525
10845
  if (!await this.io.readText(join(targetDir, "SKILL.md"))) return {
10526
10846
  ok: false,
10527
- message: `Skill "${targetName}" not found.`
10847
+ message: `技能「${targetName}」不存在。`
10528
10848
  };
10529
10849
  for (const source of normalizedSources) {
10530
10850
  if (plannedSourceBytes.has(source)) {
@@ -10587,7 +10907,7 @@ var SkillLibrary = class {
10587
10907
  if (!result.ok) throw new Error(result.message);
10588
10908
  return {
10589
10909
  ok: true,
10590
- message: `Consolidated ${normalizedSources.join(", ")} into "${targetName}".`,
10910
+ message: `已把 ${normalizedSources.join(", ")} 合并进「${targetName}」。`,
10591
10911
  path: targetDir
10592
10912
  };
10593
10913
  } catch (error) {
@@ -10600,11 +10920,11 @@ var SkillLibrary = class {
10600
10920
  }
10601
10921
  if (failedRestores.length > 0) return {
10602
10922
  ok: false,
10603
- message: `Consolidation failed (${reason}); rolled back EXCEPT ${failedRestores.join(", ")} — still in .archive, restore them with /evolution skill restore.`
10923
+ message: `合并失败(${reason});已回滚,但以下未回滚:${failedRestores.join(", ")}:它们仍在 .archive 里,请用 /evolution skill restore 恢复。`
10604
10924
  };
10605
10925
  return {
10606
10926
  ok: false,
10607
- message: `Consolidation failed and was rolled back: ${reason}`
10927
+ message: `合并失败,已回滚:${reason}`
10608
10928
  };
10609
10929
  }
10610
10930
  });
@@ -10633,21 +10953,21 @@ var SkillLibrary = class {
10633
10953
  };
10634
10954
  if (moves.length === 0) return {
10635
10955
  ok: false,
10636
- message: "Restructure requires at least one section move."
10956
+ message: "重构至少需要一次章节搬移。"
10637
10957
  };
10638
10958
  if (moves.length > 5) return {
10639
10959
  ok: false,
10640
- message: `Restructure exceeds 5 moves.`
10960
+ message: `重构超过 5 次搬移。`
10641
10961
  };
10642
10962
  for (const move of moves) {
10643
10963
  const raw = move;
10644
10964
  if (raw === null || typeof raw !== "object") return {
10645
10965
  ok: false,
10646
- message: "Every restructure move must be an object with a heading."
10966
+ message: "每次重构搬移都必须是带 heading 的对象。"
10647
10967
  };
10648
10968
  if (typeof move.heading !== "string" || !move.heading.trim()) return {
10649
10969
  ok: false,
10650
- message: "Every restructure move needs a non-empty heading."
10970
+ message: "每次重构搬移都需要非空的 heading。"
10651
10971
  };
10652
10972
  const targetIssue = validateRestructureTarget(move.toFile);
10653
10973
  if (targetIssue) return {
@@ -10659,25 +10979,25 @@ var SkillLibrary = class {
10659
10979
  const md = await this.io.readText(join(dir, "SKILL.md"));
10660
10980
  if (!md) return {
10661
10981
  ok: false,
10662
- message: `Skill "${name}" not found.`
10982
+ message: `技能「${name}」不存在。`
10663
10983
  };
10664
10984
  const block = frontmatterBlock(md);
10665
10985
  if (!block) return {
10666
10986
  ok: false,
10667
- message: "SKILL.md has no valid frontmatter; refusing to restructure."
10987
+ message: "SKILL.md 没有有效的 frontmatter;拒绝重构。"
10668
10988
  };
10669
10989
  const header = block.lines.slice(0, block.end + 1).join("\n");
10670
10990
  const plan = planRestructureSections(md.slice(header.length).replace(/\r\n/g, "\n"), moves);
10671
10991
  if ("error" in plan) return {
10672
10992
  ok: false,
10673
- message: `Restructure rejected: ${plan.error}`
10993
+ message: `重构被拒绝:${plan.error}`
10674
10994
  };
10675
10995
  const newMd = `${header}${plan.body}`.replace(/\r\n/g, "\n");
10676
10996
  const finalMd = block.nl === "\r\n" ? newMd.replace(/\n/g, "\r\n") : newMd;
10677
10997
  const newMdCheck = validateFrontmatter(finalMd, name, this.limits);
10678
10998
  if (newMdCheck) return {
10679
10999
  ok: false,
10680
- message: `Restructure rejected: ${newMdCheck}`
11000
+ message: `重构被拒绝:${newMdCheck}`
10681
11001
  };
10682
11002
  const listing = (this.limits.citationPolicy ?? "verify") === "refuse" ? null : await this.listSupportFiles(name);
10683
11003
  const files = listing !== null && isPresent(listing) ? listing.value : null;
@@ -10686,7 +11006,7 @@ var SkillLibrary = class {
10686
11006
  if (refs.length === 0) continue;
10687
11007
  if (files === null) return {
10688
11008
  ok: false,
10689
- message: `Restructure rejected: section "## ${section.heading}" references support files (${refs.join(", ")}) that stay behind — split the section or move it with its files.`
11009
+ message: `重构被拒绝:章节「## ${section.heading}」引用了会留在原地的支撑文件(${refs.join(", ")}):请拆开该章节,或连同它的文件一起搬。`
10690
11010
  };
10691
11011
  const report = resolveCitations({
10692
11012
  content: section.text,
@@ -10697,7 +11017,7 @@ var SkillLibrary = class {
10697
11017
  const missing = report.dangling.map((ref) => ref.target ?? ref.raw).join(", ");
10698
11018
  return {
10699
11019
  ok: false,
10700
- message: `Restructure rejected: section "## ${section.heading}" cites support file(s) that do not exist (${missing}) — fix the citation or create the file first.`
11020
+ message: `重构被拒绝:章节「## ${section.heading}」引用的支撑文件不存在(${missing}):请修正引用,或先创建该文件。`
10701
11021
  };
10702
11022
  }
10703
11023
  }
@@ -10738,7 +11058,7 @@ var SkillLibrary = class {
10738
11058
  if (!result.ok) return result;
10739
11059
  return {
10740
11060
  ok: true,
10741
- message: `Restructured "${name}": moved ${plan.sections.length} section(s) to references/.`,
11061
+ message: `已重构「${name}」:把 ${plan.sections.length} 个章节搬到 references/。`,
10742
11062
  path: dir
10743
11063
  };
10744
11064
  }
@@ -10760,12 +11080,12 @@ var SkillLibrary = class {
10760
11080
  const md = await this.io.readText(join(dir, "SKILL.md"));
10761
11081
  if (!md) return {
10762
11082
  ok: false,
10763
- message: `Skill "${name}" not found.`
11083
+ message: `技能「${name}」不存在。`
10764
11084
  };
10765
11085
  const protection = plan.protection === "write" ? await this.writeProtection(name, plan.origin) : plan.protection === "delete" ? await this.deleteProtection(name) : null;
10766
11086
  if (protection) return {
10767
11087
  ok: false,
10768
- message: `Skill "${name}" is protected (${protection}).`
11088
+ message: `技能「${name}」受保护(${protection})。`
10769
11089
  };
10770
11090
  const landing = [];
10771
11091
  for (const write of plan.writes) {
@@ -10775,12 +11095,12 @@ var SkillLibrary = class {
10775
11095
  } catch (error) {
10776
11096
  return {
10777
11097
  ok: false,
10778
- message: `Tree change refused: cannot safely pre-read ${write.target} (${error instanceof Error ? error.message : String(error)}); no writes were performed`
11098
+ message: `拒绝目录树变更:无法安全预读 ${write.target}(${error instanceof Error ? error.message : String(error)});未执行任何写入`
10779
11099
  };
10780
11100
  }
10781
11101
  if (Buffer.byteLength(write.content, "utf8") > this.limits.maxSkillFileBytes) return {
10782
11102
  ok: false,
10783
- message: `Write exceeds ${this.limits.maxSkillFileBytes} bytes: ${write.target}`
11103
+ message: `写入超过 ${this.limits.maxSkillFileBytes} 字节:${write.target}`
10784
11104
  };
10785
11105
  const threat = this.contentThreatBlock(write.content);
10786
11106
  if (threat) return {
@@ -10835,17 +11155,17 @@ var SkillLibrary = class {
10835
11155
  const reason = error instanceof Error ? error.message : String(error);
10836
11156
  if (stuck.length === 0) return {
10837
11157
  ok: false,
10838
- message: `Tree change failed and was rolled back: ${reason}`
11158
+ message: `目录树变更失败,已回滚:${reason}`
10839
11159
  };
10840
11160
  return {
10841
11161
  ok: false,
10842
- message: `Tree change failed: ${reason}. The rollback could not restore ${stuck.join(", ")} — recover those targets from the .backups snapshot (or re-apply the change).`
11162
+ message: `目录树变更失败:${reason}。回滚无法恢复 ${stuck.join(", ")}:请从 .backups 快照恢复这些目标(或重新应用这次变更)。`
10843
11163
  };
10844
11164
  }
10845
11165
  const skillMdWrite = landing.find((entry) => entry.target.split(/[\\/]/).pop() === "SKILL.md");
10846
11166
  const auditedBefore = skillMdWrite === void 0 ? md : skillMdWrite.previous;
10847
11167
  const auditedAfter = skillMdWrite === void 0 ? md : skillMdWrite.content;
10848
- await this.audit(name, plan.auditAction, auditedBefore, auditedAfter, plan.auditSummary);
11168
+ await this.audit(name, plan.auditAction, auditedBefore, auditedAfter, plan.auditSummary, void 0, plan.origin);
10849
11169
  this.notifyMutation({
10850
11170
  action: plan.eventAction,
10851
11171
  name,
@@ -10853,7 +11173,7 @@ var SkillLibrary = class {
10853
11173
  });
10854
11174
  return {
10855
11175
  ok: true,
10856
- message: durabilityWarning === "" ? `${plan.eventAction} "${name}" succeeded.` : `${plan.eventAction} "${name}" succeeded (warning: the write landed but the directory fsync failed — durability unconfirmed: ${durabilityWarning})`,
11176
+ message: durabilityWarning === "" ? `${plan.eventAction} "${name}" succeeded.` : `${plan.eventAction} "${name}" succeeded (警告:写入已落盘,但目录 fsync 失败 —— 持久性未确认:${durabilityWarning})`,
10857
11177
  path: dir
10858
11178
  };
10859
11179
  }
@@ -10872,7 +11192,7 @@ var SkillLibrary = class {
10872
11192
  const dest = this.dirOf(name);
10873
11193
  if (await this.io.exists(dest)) return {
10874
11194
  ok: false,
10875
- message: await this.io.exists(join(dest, "SKILL.md")) ? `Skill "${name}" already exists in the active root; refusing to overwrite.` : `Skill directory "${name}" already exists in the active root but carries no SKILL.md; remove or repair it before restoring.`
11195
+ message: await this.io.exists(join(dest, "SKILL.md")) ? `技能「${name}」已存在于当前根目录;拒绝覆盖。` : `技能目录「${name}」已存在于当前根目录,但没有 SKILL.md;恢复前请先移除或修复它。`
10876
11196
  };
10877
11197
  let caseVariant;
10878
11198
  try {
@@ -10880,7 +11200,7 @@ var SkillLibrary = class {
10880
11200
  } catch {}
10881
11201
  if (caseVariant !== void 0) return {
10882
11202
  ok: false,
10883
- message: `Skill "${caseVariant}" (same name, different letter case) already exists in the active root; restoring "${name}" beside it would create ambiguous duplicates — restore as "${caseVariant}" or remove the variant first.`
11203
+ message: `技能「${caseVariant}」(同名、仅大小写不同)已存在于当前根目录;把「${name}」恢复到它旁边会造成含义不清的重复:请恢复为「${caseVariant}」,或先移除该变体。`
10884
11204
  };
10885
11205
  const archiveRoot = join(this.root, ".archive");
10886
11206
  let entries;
@@ -10889,7 +11209,7 @@ var SkillLibrary = class {
10889
11209
  } catch {
10890
11210
  return {
10891
11211
  ok: false,
10892
- message: "No skill archive available."
11212
+ message: "没有可用的技能归档。"
10893
11213
  };
10894
11214
  }
10895
11215
  const candidates = entries.filter((entry) => entry === name || entry.startsWith(`${name}-`)).sort().reverse();
@@ -10924,28 +11244,28 @@ var SkillLibrary = class {
10924
11244
  if (!chosen) {
10925
11245
  if (unreadable.length === 0) return {
10926
11246
  ok: false,
10927
- message: `Skill "${name}" is not in .archive.`
11247
+ message: `技能「${name}」不在 .archive 里。`
10928
11248
  };
10929
11249
  return {
10930
11250
  ok: false,
10931
- message: `No archived entry for "${name}" carries a matching frontmatter name, but .archive holds ${unreadable.length} entr(y/ies) named like it: ${unreadable.join(", ")}. Those entries have no readable SKILL.md (or none matching the name), so restoring by name cannot tell them apart — rename the directory to the skill name and restore again, or restore from a snapshot.`
11251
+ message: `.archive 里没有「${name}」对应的 frontmatter 名,但有 ${unreadable.length} 个名字相似的归档项:${unreadable.join(", ")}。这些归档项没有可读的 SKILL.md(或名字对不上),按名字恢复无法区分它们:请把目录改成技能名后再次恢复,或改用快照恢复。`
10932
11252
  };
10933
11253
  }
10934
11254
  const source = join(archiveRoot, chosen);
10935
11255
  if (this.io.isSymlink) {
10936
11256
  if (await this.io.isSymlink(source) === true) return {
10937
11257
  ok: false,
10938
- message: `Archived entry "${chosen}" is a symlink; refusing to restore it.`
11258
+ message: `归档项「${chosen}」是符号链接;拒绝恢复。`
10939
11259
  };
10940
11260
  }
10941
11261
  if (await this.supportDirsUnlistable(dest)) return {
10942
11262
  ok: false,
10943
- message: `Skill "${name}" could not be checked for writers (a support directory could not be listed); retry restoring.`
11263
+ message: `无法检查技能「${name}」是否有写入方(有一个支撑目录列不出来):请重试恢复。`
10944
11264
  };
10945
11265
  const moveFailure = await this.moveDir(source, dest);
10946
11266
  if (moveFailure !== void 0) return {
10947
11267
  ok: false,
10948
- message: `Restore of "${name}" from .archive failed: ${moveFailure}`
11268
+ message: `从 .archive 恢复「${name}」失败:${moveFailure}`
10949
11269
  };
10950
11270
  for (const marker of [".archive-reason", ".archive-name"]) if (await this.io.exists(join(dest, marker))) await this.io.remove(join(dest, marker));
10951
11271
  const restoredProbe = await probeText(this.io, join(dest, "SKILL.md"));
@@ -10958,7 +11278,7 @@ var SkillLibrary = class {
10958
11278
  });
10959
11279
  return {
10960
11280
  ok: true,
10961
- message: `Skill "${name}" restored from .archive.`,
11281
+ message: `技能「${name}」已从 .archive 恢复。`,
10962
11282
  path: dest
10963
11283
  };
10964
11284
  }
@@ -10975,12 +11295,12 @@ var SkillLibrary = class {
10975
11295
  const dir = this.dirOf(name);
10976
11296
  if (!await this.io.exists(join(dir, "SKILL.md"))) return {
10977
11297
  ok: false,
10978
- message: `Skill "${name}" not found.`
11298
+ message: `技能「${name}」不存在。`
10979
11299
  };
10980
11300
  const protection = await this.writeProtection(name, origin);
10981
11301
  if (protection) return {
10982
11302
  ok: false,
10983
- message: `Skill "${name}" is protected (${protection}).`
11303
+ message: `技能「${name}」受保护(${protection})。`
10984
11304
  };
10985
11305
  const validation = validateSupportPath(filePath);
10986
11306
  if (validation) return {
@@ -10989,7 +11309,7 @@ var SkillLibrary = class {
10989
11309
  };
10990
11310
  if (Buffer.byteLength(content, "utf8") > this.limits.maxSkillFileBytes) return {
10991
11311
  ok: false,
10992
- message: `Support file exceeds ${this.limits.maxSkillFileBytes} bytes.`
11312
+ message: `支撑文件超过 ${this.limits.maxSkillFileBytes} 字节。`
10993
11313
  };
10994
11314
  const threat = this.contentThreatBlock(content);
10995
11315
  if (threat) return {
@@ -11009,14 +11329,14 @@ var SkillLibrary = class {
11009
11329
  if (overCap && !repairing && this.supportFileCharPolicy() === "enforce") return {
11010
11330
  result: {
11011
11331
  ok: false,
11012
- message: `Support file "${filePath}" content exceeds ${this.limits.maxSkillContentChars} characters. ${CONTENT_SPLIT_HINT}`
11332
+ message: `支撑文件「${filePath}」的内容超过 ${this.limits.maxSkillContentChars} 个字符。${CONTENT_SPLIT_HINT}`
11013
11333
  },
11014
11334
  write: null
11015
11335
  };
11016
11336
  if (current !== null && content.trimEnd() === current.trimEnd()) return {
11017
11337
  result: {
11018
11338
  ok: true,
11019
- message: `Support file "${filePath}" unchanged: the supplied content already matches the current file; nothing written.`,
11339
+ message: `支撑文件「${filePath}」未变更:提供的内容与当前文件一致,未写入。`,
11020
11340
  noop: true,
11021
11341
  path: target
11022
11342
  },
@@ -11025,7 +11345,7 @@ var SkillLibrary = class {
11025
11345
  return {
11026
11346
  result: {
11027
11347
  ok: true,
11028
- message: `Support file "${filePath}" written to "${name}".${capAdvisory}`,
11348
+ message: `支撑文件「${filePath}」已写入「${name}」。${capAdvisory}`,
11029
11349
  path: target
11030
11350
  },
11031
11351
  write: content,
@@ -11059,12 +11379,12 @@ var SkillLibrary = class {
11059
11379
  const dir = this.dirOf(name);
11060
11380
  if (!await this.io.exists(join(dir, "SKILL.md"))) return {
11061
11381
  ok: false,
11062
- message: `Skill "${name}" not found.`
11382
+ message: `技能「${name}」不存在。`
11063
11383
  };
11064
11384
  const protection = await this.writeProtection(name, origin);
11065
11385
  if (protection) return {
11066
11386
  ok: false,
11067
- message: `Skill "${name}" is protected (${protection}).`
11387
+ message: `技能「${name}」受保护(${protection})。`
11068
11388
  };
11069
11389
  const validation = validateSupportPath(filePath);
11070
11390
  if (validation) return {
@@ -11076,7 +11396,7 @@ var SkillLibrary = class {
11076
11396
  if (anchor !== void 0) return anchorRefusalFile(name, filePath, "missing");
11077
11397
  return {
11078
11398
  ok: false,
11079
- message: `File "${filePath}" not found in skill "${name}".`
11399
+ message: `文件「${filePath}」不在技能「${name}」里。`
11080
11400
  };
11081
11401
  }
11082
11402
  const beforeProbe = await probeText(this.io, target);
@@ -11100,7 +11420,7 @@ var SkillLibrary = class {
11100
11420
  });
11101
11421
  if (!ran.done) return {
11102
11422
  ok: false,
11103
- message: "internal error: the delete transaction did not invoke the task; nothing was removed"
11423
+ message: "内部错误:删除事务没有调用任务;没有删除任何东西"
11104
11424
  };
11105
11425
  } else if (verdict === "match") await this.io.remove(target);
11106
11426
  if (verdict !== "match") return anchorRefusalFile(name, filePath, verdict);
@@ -11113,7 +11433,7 @@ var SkillLibrary = class {
11113
11433
  });
11114
11434
  return {
11115
11435
  ok: true,
11116
- message: `Support file "${filePath}" removed from "${name}".`,
11436
+ message: `支撑文件「${filePath}」已从「${name}」移除。`,
11117
11437
  path: target
11118
11438
  };
11119
11439
  }
@@ -11331,7 +11651,7 @@ var SkillLibrary = class {
11331
11651
  out.push({
11332
11652
  path: join(backupRoot, name),
11333
11653
  createdAt: "",
11334
- reason: "unreadable or missing manifest (orphan snapshot)"
11654
+ reason: "清单读不出来或缺失(孤儿快照)"
11335
11655
  });
11336
11656
  continue;
11337
11657
  }
@@ -11373,12 +11693,12 @@ var SkillLibrary = class {
11373
11693
  const listed = await this.listSnapshots();
11374
11694
  if (isUnknown(listed)) return {
11375
11695
  ok: false,
11376
- message: `Snapshot restore refused: the snapshot directory could not be read (${listed.reason}) — nothing was changed.`
11696
+ message: `拒绝快照恢复:快照目录读不出来(${listed.reason})—— 未做任何改动。`
11377
11697
  };
11378
11698
  const latest = isPresent(listed) ? listed.value[0] : void 0;
11379
11699
  if (!latest) return {
11380
11700
  ok: false,
11381
- message: "No skill snapshot available."
11701
+ message: "没有可用的技能快照。"
11382
11702
  };
11383
11703
  let preRollbackPath;
11384
11704
  try {
@@ -11386,7 +11706,7 @@ var SkillLibrary = class {
11386
11706
  } catch (error) {
11387
11707
  return {
11388
11708
  ok: false,
11389
- message: `Snapshot restore was refused before anything was cleared: taking the pre-rollback snapshot failed (${error instanceof Error ? error.message : String(error)}) — the active tree is UNCHANGED. Resolve the snapshot write failure and retry.`
11709
+ message: `在任何清理之前就拒绝了快照恢复:生成回滚前快照失败(${error instanceof Error ? error.message : String(error)})—— 活动目录树未改变。请解决快照写入失败后重试。`
11390
11710
  };
11391
11711
  }
11392
11712
  let snapshotExtras;
@@ -11395,7 +11715,7 @@ var SkillLibrary = class {
11395
11715
  } catch (error) {
11396
11716
  return {
11397
11717
  ok: false,
11398
- message: `Snapshot restore was refused before anything was cleared: reading the snapshot manifest/extras failed (${error instanceof Error ? error.message : String(error)}) — the active tree is UNCHANGED. Resolve the read failure and retry.`
11718
+ message: `在任何清理之前就拒绝了快照恢复:读取快照清单或附属文件失败(${error instanceof Error ? error.message : String(error)})—— 活动目录树未改变。请解决读取失败后重试。`
11399
11719
  };
11400
11720
  }
11401
11721
  try {
@@ -11406,17 +11726,17 @@ var SkillLibrary = class {
11406
11726
  await this.restoreSnapshotIntoRoot(preRollbackPath);
11407
11727
  return {
11408
11728
  ok: false,
11409
- message: `Snapshot restore failed (${reason}); the active tree was rolled back to the pre-rollback snapshot.`
11729
+ message: `快照恢复失败(${reason});活动目录树已回滚到回滚前的快照。`
11410
11730
  };
11411
11731
  } catch (rollbackError) {
11412
11732
  const rb = rollbackError instanceof Error ? rollbackError.message : String(rollbackError);
11413
11733
  if (isPreClearRefusal(reason) && isPreClearRefusal(rb)) return {
11414
11734
  ok: false,
11415
- message: `Snapshot restore was refused before anything was cleared; the active tree is UNCHANGED.\n- target: ${reason}\n- pre-rollback: ${rb}\nResolve the cause (a live skill write, or an incomplete snapshot) and retry — no manual rescue is needed.`
11735
+ message: `在任何清理之前就拒绝了快照恢复; 活动目录树未改变。\n- 目标:${reason}\n- 回滚前快照:${rb}\n请解决原因(有存活的技能写入,或快照不完整)后重试 —— 无需手动抢救。`
11416
11736
  };
11417
11737
  return {
11418
11738
  ok: false,
11419
- message: `Snapshot restore failed (${reason}) AND pre-rollback restore failed (${rb}). Rescue manually from: ${preRollbackPath} (pre-rollback), ${latest.path} (target).`
11739
+ message: `快照恢复失败(${reason}),且回滚前快照的恢复也失败(${rb})。请手动抢救:${preRollbackPath}(回滚前)、${latest.path}(目标)。`
11420
11740
  };
11421
11741
  }
11422
11742
  }
@@ -11426,7 +11746,7 @@ var SkillLibrary = class {
11426
11746
  });
11427
11747
  return {
11428
11748
  ok: true,
11429
- message: `Restored skill tree from ${latest.path}`,
11749
+ message: `已从 ${latest.path} 恢复技能目录树`,
11430
11750
  path: latest.path,
11431
11751
  ...snapshotExtras.length === 0 ? {} : { extras: snapshotExtras }
11432
11752
  };
@@ -13074,7 +13394,7 @@ function planNamespaceMigration(documentText) {
13074
13394
  skipped.push({
13075
13395
  namespace,
13076
13396
  key,
13077
- reason: "row `" + rowId + "` has no such parameter"
13397
+ reason: "row `" + rowId + "` 没有这个参数"
13078
13398
  });
13079
13399
  continue;
13080
13400
  }
@@ -13126,7 +13446,7 @@ function classifyNamespaceMigration(plan, read) {
13126
13446
  for (const value of entry.values) skipped.push({
13127
13447
  namespace: entry.namespace,
13128
13448
  key: value.source,
13129
- reason: "row `" + entry.rowId + "` is not served in this composition"
13449
+ reason: "row `" + entry.rowId + "` 在本组合中不提供服务"
13130
13450
  });
13131
13451
  continue;
13132
13452
  }
@@ -13439,4 +13759,4 @@ function foldUntrustedLines(text, limit) {
13439
13759
  return limit > 0 ? marked.slice(0, limit) : marked;
13440
13760
  }
13441
13761
  //#endregion
13442
- export { AGENT_PRESET_PACKAGE, AGENT_PRESET_ROOT_HINT, AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_PROBE_TIMEOUT_MS, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_RUN_RECORDS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, EMPTY_PATCH_SEED, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, FUZZY_MAX_PATTERN_CHARS, FUZZY_MAX_WORK, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, INSTANCE_KEYS, KEEP_LINE_RE, LONG_RUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_REQUEST_BODY_BYTES, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, NAMESPACE_MIGRATIONS, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, PLATFORM_SERVICE_PROBES, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, RUNS_SCHEMA_VERSION, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPRESSED_FILE_VERSION, SkillLibrary, TEXT_DIFF_MAX_CHARS, TEXT_DIFF_MAX_LINES, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UNTRUSTED_LINE_BREAK, UNTRUSTED_LINE_MARK, UPSTREAM_LIMIT_CHARS_PER_TOKEN, VERSION_SUMMARY_MAX_CHARS, absentHostServices, advanceReview, affinity, ageCopyKey, allowRowCollisions, anchorVerdict, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyNamespaceMigration, applyReferenceRewrite, asRecord, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, atomicWriteFiles, authoringFeedback, basePresetPlugins, blobPath, bodyCost, buildCuratorRunReport, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, canonicalWriteId, claimInstance, clampOnce, clampedNumber, classifyNamespaceMigration, clearReviewChannel, collectReadSkillNames, composePresetComposition, composePresetEntry, composePresetInsert, composePresetRow, compositionRowIds, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, createGateSet, daysSinceIso, declaredReportStamp, denseBlockSequence, describeReferenceRewrite, dispatchKeyOf, dispatchStepOf, displayBodyOf, duplicateHeadings, elapsedSince, emptyRecord, entryTarget, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionActivityProjection, evolutionEventPayloadIssue, evolutionEvidenceProjection, evolutionHome, evolutionIoAdapter, evolutionReadsProjection, evolutionRoot, evolutionSignalsProjection, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurnEvent, foldTurnEvents, foldUntrustedLines, frontmatterBlock, frontmatterCatalogInvalid, fuzzyPatch, getRecord, historyIndexFile, historyRoot, identity, idleDays, initialTurnFold, installEvolutionProjections, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isBookkeepingFrame, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isLoopbackRequest, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isRecord, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, latestActivityAt, latestVersionAt, lifecycleCandidate, listDirectories, listEventArchives, liveBool, liveEnum, liveNumber, liveString, loadMutations, loadRowOverrides, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, mergePresetRow, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, neutralizePromptVariables, newRunRegistry, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, normalizeVersionSummary, observeEvent, orderVersions, overlongLines, paramNamespace, paramRowId, paramSettingsId, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, partitionVersions, payloadText, persistedWriteSite, persistedWriteSites, pickWithPolicy, planNamespaceMigration, planReferenceRewrite, planRehoming, policyStageLimits, presetPatchText, presetRowBlock, presetRowBody, presetRowId, probeAbsent, probeDesktopScope, probeList, probeMtime, probePresent, probeReason, probeText, probeUnknown, projectionText, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readJsonBody, readJsonObject, readNumberParam, readParam, readingStats, recordMutation, recordVersions, redactSecrets, relatedSkillNames, releaseInstance, removePresetRow, renameWithRetry, renderCuratorReportMarkdown, renderLongRunTable, reportTime, reportsSweepLockTarget, resolveBasePresetPatch, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, retainEventArchives, retirementReport, reviewPrompt, rowBlockSpan, rowSiblingEnd, runsFile, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopedProbeReport, sessionAudited, sessionEvidenceIndex, sessionLastEventTime, sessionReadNames, sessionReadSkillNames, sessionSeesFamilyTools, sessionTurnSignals, skillReadNameOf, skillRowCells, skillStateKey, skillsRoot, supportFileReadOf, suppressedFile, sweepReports, sweepReviewChannelSessions, textDiffFacts, tokenLineFor, transactIo, transactTaskGuard, trimPatternBoundaries, trimTrailingBlankLines, unhookedSupportPointers, unwrapLive, updateSuppressedNames, usageFile, usageObserved, userSetKeys, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionActionKind, versionRowCells, versionSummaryPrompt, versionTarget, vocabulary, writeDurableTmp, writeJson, writePathSymbols, yamlPlainScalarNeedsQuotes };
13762
+ export { AGENT_PRESET_PACKAGE, AGENT_PRESET_ROOT_HINT, AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ADMISSION, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_PROBE_TIMEOUT_MS, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_RUN_RECORDS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, EMPTY_PATCH_SEED, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, FUZZY_MAX_PATTERN_CHARS, FUZZY_MAX_WORK, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, INSTANCE_KEYS, KEEP_LINE_RE, LONG_RUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_REQUEST_BODY_BYTES, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, NAMESPACE_MIGRATIONS, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, PLATFORM_SERVICE_PROBES, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, RULES, RUNS_SCHEMA_VERSION, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPORT_SPRAWL_FILES, SUPPRESSED_FILE_VERSION, SkillLibrary, TEXT_DIFF_MAX_CHARS, TEXT_DIFF_MAX_LINES, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UNTRUSTED_LINE_BREAK, UNTRUSTED_LINE_MARK, UPSTREAM_LIMIT_CHARS_PER_TOKEN, VERSION_SUMMARY_MAX_CHARS, absentHostServices, admissionVerdict, advanceReview, affinity, ageCopyKey, allowRowCollisions, anchorVerdict, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyNamespaceMigration, applyReferenceRewrite, asRecord, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, atomicWriteFiles, authoringFeedback, basePresetPlugins, blobPath, bodyCost, buildCuratorRunReport, buildHealthBaseline, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, canonicalWriteId, claimInstance, clampOnce, clampedNumber, classifyNamespaceMigration, clearReviewChannel, collectReadSkillNames, composePresetComposition, composePresetEntry, composePresetInsert, composePresetRow, compositionRowIds, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, countReferenceFiles, createGateSet, daysSinceIso, declaredReportStamp, denseBlockSequence, describeReferenceRewrite, diffFindings, dispatchKeyOf, dispatchStepOf, displayBodyOf, duplicateHeadings, elapsedSince, emptyRecord, entryTarget, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionActivityProjection, evolutionEventPayloadIssue, evolutionEvidenceProjection, evolutionHome, evolutionIoAdapter, evolutionReadsProjection, evolutionRoot, evolutionSignalsProjection, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurnEvent, foldTurnEvents, foldUntrustedLines, frontmatterBlock, frontmatterCatalogInvalid, fuzzyPatch, getRecord, h2Sections, historyIndexFile, historyRoot, identity, idleDays, initialTurnFold, installEvolutionProjections, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isBookkeepingFrame, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isLoopbackRequest, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isRecord, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, latestActivityAt, latestVersionAt, lifecycleCandidate, lintBody, lintSkill, listDirectories, listEventArchives, liveBool, liveEnum, liveNumber, liveString, loadMutations, loadRowOverrides, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, mergePresetRow, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, neutralizePromptVariables, newRunRegistry, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, normalizeVersionSummary, observeEvent, orderVersions, overlongLines, paramNamespace, paramRowId, paramSettingsId, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, partitionVersions, payloadText, persistedWriteSite, persistedWriteSites, pickWithPolicy, planNamespaceMigration, planReferenceRewrite, planRehoming, policyStageLimits, presetPatchText, presetRowBlock, presetRowBody, presetRowId, probeAbsent, probeDesktopScope, probeList, probeMtime, probePresent, probeReason, probeText, probeUnknown, projectionText, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readJsonBody, readJsonObject, readNumberParam, readParam, readingStats, recordMutation, recordVersions, redactSecrets, relatedSkillNames, releaseInstance, removePresetRow, renameWithRetry, renderCuratorReportMarkdown, renderLongRunTable, reportTime, reportsSweepLockTarget, resolveBasePresetPatch, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, restructureCandidates, retainEventArchives, retirementReport, reviewPrompt, rowBlockSpan, rowSiblingEnd, runsFile, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopedProbeReport, sessionAudited, sessionEvidenceIndex, sessionLastEventTime, sessionReadNames, sessionReadSkillNames, sessionSeesFamilyTools, sessionTurnSignals, skillReadNameOf, skillRowCells, skillStateKey, skillsRoot, supportFileReadOf, suppressedFile, sweepReports, sweepReviewChannelSessions, textDiffFacts, tokenLineFor, transactIo, transactTaskGuard, trimPatternBoundaries, trimTrailingBlankLines, unhookedSupportPointers, unwrapLive, updateSuppressedNames, usageFile, usageObserved, userSetKeys, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionActionKind, versionRowCells, versionSummaryPrompt, versionTarget, vocabulary, writeDurableTmp, writeJson, writePathSymbols, yamlPlainScalarNeedsQuotes };