@shgroup/dsh-serenity-hooks 1.52.3 → 1.54.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/README.md CHANGED
@@ -76,6 +76,7 @@ dsh plugin --profile web add link:$(pwd)/hooks/dsh-serenity-hooks
76
76
  | `msm` | 小工具执行入口:`msm("名字", ["参数"])`;名字记不全就给候选;`inspect=true` 看用法 | 调用工作区里注册的任何小工具 |
77
77
  | `praxis` | 按需给 AI 注入四套"做事方法":输出自检(eap)、设计对齐(neat)、认知连续性(cce)、本体工程(ontology) | 要它把话说清楚 / 先对齐再动手 / 动手前先把领域结构显式化时 |
78
78
  | `ontology` | 本体工程**引擎**(能跑的那个):读 `.ttl` 本体文件,做结构校验(M1–M10,含公理表达式语法)、生成摘要、查声明与实况是否一致(V1–V4)、出语义审查提示词、出"公理 ↔ 技能"合规清单;还能写一份新本体的模板 | 想给某个领域建/校验本体时。⚠️ 与 `praxis ontology` 是**同一领域的两个面**:那个是**方法文本**(怎么建模),这个是**引擎**(真去检查)。路径必须在工作区内 |
79
+ | `serenity-skill-creator` | 增强型 skill 标准(**serenity-skill**)的 **creator**:`guide`(编写与优化方法,先读它)/`create`(生成 skill 骨架 = SKILL.md + skill.ttl + references/,**基础链的相对路径由它算**)/`check`(**A1 结构 + A2 链上基础链**,三态可分辨:已建模通过/普通 skill/建模了但检查没跑成)/`review`(**C 档**:文字与本体逐条的合规问题清单,半机械,要人回答)/`index`(预览**模型实际会看到**的第二层参考件清单)/`status`(全容器一览:哪些已建模) | 新建或改造 skill、给 skill 建本体、想知道"模型到底看不看得见我那些参考件"时 |
79
80
  | `handyman` | 杂工:派一个便宜模型的助手干活。**默认模式(foreground)**= 一次串行委派、拿回结果;**background 模式**= 循环干活直到完成(完成码校验 / 轮次上限 / 自动重启 / 进度文件),也可一次派多个并行 | 大批量、重复性的活(扫描几十个技能、逐用例回归) |
80
81
  | `localstore` | 存密钥和配置(凭据、偏好两个命名空间) | API key、密码集中放一处,不进 git |
81
82
  | `container_admin` | 机务舱:管理子角色、管理小工具注册表、查看全部配置 | 定义"子角色"、注册新小工具、改配置时 |
package/dsh.plugin.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "id": "dsh-serenity-hooks",
3
- "version": "1.52.3",
3
+ "version": "1.54.0",
4
4
  "main": "lib/index.js",
5
- "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——13 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/handyman/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1(deepseek-ai/deepseek-harness)。",
5
+ "description": "宁静号 ACC harness(Native Cordis 插件):给 DSH 装一个「AI 工作区」——14 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/serenity-skill-creator/handyman/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫)+ 轨迹日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1(deepseek-ai/deepseek-harness)。",
6
6
  "engines": {
7
7
  "dsh": ">=0.1.7-rc.1"
8
8
  },
@@ -20,7 +20,8 @@
20
20
  "im-bridge",
21
21
  "acc-diag",
22
22
  "diagram",
23
- "ontology"
23
+ "ontology",
24
+ "serenity-skill-creator"
24
25
  ],
25
26
  "skills": []
26
27
  }
package/lib/index.js CHANGED
@@ -424,7 +424,7 @@ function runCcFs(root, args) {
424
424
  * 进程内注册(取代 v0.1 的 bash spawn runner):zod/schemastery 参数校验、
425
425
  * 规范 JSON 输出、纯 render 投影。逻辑在 fs-ops.ts(可单测)。
426
426
  */
427
- function renderText$10(value) {
427
+ function renderText$11(value) {
428
428
  return [{
429
429
  type: "text",
430
430
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -496,7 +496,7 @@ const ccFsTool = defineTool({
496
496
  },
497
497
  output: {
498
498
  schema: { type: "json" },
499
- render: (args, value) => renderText$10(value)
499
+ render: (args, value) => renderText$11(value)
500
500
  },
501
501
  async execute(args, exec) {
502
502
  const root = cccRootForExec(exec);
@@ -572,7 +572,7 @@ async function runKit(root, args, hostCtx) {
572
572
  /**
573
573
  * kit.ts — dashboard 真实 DSH 工具定义(defineTool)(v1.30:acc_kit → dashboard)
574
574
  */
575
- function renderText$9(value) {
575
+ function renderText$10(value) {
576
576
  return [{
577
577
  type: "text",
578
578
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -600,7 +600,7 @@ function createKitTool(ctx) {
600
600
  },
601
601
  output: {
602
602
  schema: { type: "json" },
603
- render: (args, value) => renderText$9(value)
603
+ render: (args, value) => renderText$10(value)
604
604
  },
605
605
  async execute(args, exec) {
606
606
  return await runKit(cccRootForExec(exec), args, ctx);
@@ -915,7 +915,7 @@ function runGit(root, args) {
915
915
  /**
916
916
  * git.ts — container_git 真实 DSH 工具定义(defineTool)(v1.30:cc_git → container_git)
917
917
  */
918
- function renderText$8(value) {
918
+ function renderText$9(value) {
919
919
  return [{
920
920
  type: "text",
921
921
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -963,7 +963,7 @@ const gitTool = defineTool({
963
963
  },
964
964
  output: {
965
965
  schema: { type: "json" },
966
- render: (args, value) => renderText$8(value)
966
+ render: (args, value) => renderText$9(value)
967
967
  },
968
968
  async execute(args, exec) {
969
969
  const root = cccRootForExec(exec);
@@ -2188,6 +2188,7 @@ function findEntrySkills(root) {
2188
2188
  out.push({
2189
2189
  name,
2190
2190
  content: readFileSync(path, "utf-8"),
2191
+ dir: dirname(path),
2191
2192
  source
2192
2193
  });
2193
2194
  };
@@ -2215,6 +2216,230 @@ function truncateContent(content, maxChars) {
2215
2216
  return content.slice(0, maxChars) + `\n... (truncated, original length ${content.length} chars)`;
2216
2217
  }
2217
2218
  //#endregion
2219
+ //#region src/skills/resource-index.ts
2220
+ /**
2221
+ * resource-index.ts — **两层披露树的第二层**:skill 参考件索引(纯逻辑,零 DSH 依赖)
2222
+ *
2223
+ * 设计真相源:`docs/serenity-skill-design.md` §4.2(六条硬性规则)+ §5.1(目录布局)。
2224
+ * 一句话:**把 skill 的参考件"有什么"告诉模型,但不把"是什么"塞进去** ——
2225
+ * 正文留给第三层(模型按需 `read`)。
2226
+ *
2227
+ * 为什么由我们生成(R↓,读宿主取证):宿主 `@deepseek-ai/dsh-skill` 的 `renderSkillContent()`
2228
+ * (`lib/index.js` L57-81)**已经**产出 `<skill_resources>` 块,并逐字写着
2229
+ * "Load referenced resources only as needed." ⇒ **两层模型是宿主自己给的**;而
2230
+ * `SkillSummary` / `SkillDefinition` **没有任何列举子文件的位**(只有 `resourceBase` 一个基址)
2231
+ * ⇒ 宿主刻意留给 provider 的正是**这一半:枚举**。
2232
+ *
2233
+ * 六条硬性规则(逐条对应设计 §4.2,改这里先改那里):
2234
+ * ① **只枚举,不注入正文** —— 索引里**绝不**出现任何参考文件的正文;
2235
+ * ② **确定性排序** = 相对路径的 **UTF-8 字节序**(🔴 **不用 mtime**:mtime 随检出漂移,
2236
+ * 会让每请求的注入文本抖动;对照 `src/cro.ts:101` 那份**落盘快照**用 {name,bytes,mtime} ——
2237
+ * **同一件事两处的取舍相反,那个差异是判据不是巧合**);
2238
+ * ③ **有界 + 响亮** —— 条数/字符各有上限,超出**必须显式写出还剩多少项**,**绝不静默截断**;
2239
+ * ④ **摘要可复现** —— 取该文件首个 `#` 标题(剥 markdown 记号);取不到 ⇒ 留空,**不编**;
2240
+ * ⑤ **本体文件特殊标注** —— `skill.ttl` 存在 ⇒ 该行标 🧬 + 顶部加一句"已建模";
2241
+ * ⑥ **范围 = `references/` 一级条目 + `skill.ttl`** —— 子目录**给名字 + 计数、不展开、不读正文**
2242
+ * (🆕 2026-10-11 由**真实输入**逼出的修订:原写"不进子目录",实测 `references/` 下确实有内容目录,
2243
+ * 只报"有 N 个子目录"不给名字 = **不可行动**;折中仍在裁决 Q2「只披露名字、不注入正文」的意图内)。
2244
+ *
2245
+ * 🔴 本文件**只认 `<skillDir>/skill.ttl`**(owner 裁决 Q6:静默、只认固定名)。
2246
+ * `references/*-domain.ttl`(旧名)一律当**普通参考件**:不特殊对待、不告警 —— 那 14 份由 CCC 自行改名。
2247
+ * 🔴 「未建模」不是异常(裁决 Q6 / 判据 J9):无 `skill.ttl` 的**普通 skill** 照常产出索引,
2248
+ * **零告警、零误差** —— 增强必须对普通 skill 完全向后兼容。
2249
+ */
2250
+ /** 第二层占位口:本 skill 的本体约束文件(固定名,设计 §5.1) */
2251
+ const SKILL_ONTOLOGY_FILE = "skill.ttl";
2252
+ /** 参考件根(一级;不进子目录) */
2253
+ const REFERENCE_DIR = "references";
2254
+ /** 摘要长度上限(防单行超长标题把表格撑坏) */
2255
+ const EXCERPT_MAX_CHARS = 60;
2256
+ /** 读文件头部字节数上限:摘标题只值读这么些(不为一行标题读整个大文件) */
2257
+ const HEAD_BYTES = 4096;
2258
+ /** 目录计数上限:防病态大树把一次注入拖慢(超出 ⇒ 报 `≥N`,仍然响亮) */
2259
+ const DIR_SCAN_MAX_FILES = 1e3;
2260
+ /**
2261
+ * 递归统计目录下的文件数与总字节(**只统计,不读内容**)。
2262
+ * 为什么递归:设计原写"不进子目录",而**真实输入**显示 `references/` 下确有内容目录
2263
+ * (`home-rhetoric/references/corpus-subject-own`/`family-profiles/references/baby-handlebook`/
2264
+ * `home-serenity/references/diagram-lab`)⇒ 只报"有 N 个子目录"而**不给名字**是不可行动的。
2265
+ * 折中:**给名字 + 计数,仍不展开、仍不读正文**(= 裁决 Q2「只披露名字、不注入正文」的意图内)。
2266
+ */
2267
+ function scanDirStats(dir) {
2268
+ let files = 0;
2269
+ let bytes = 0;
2270
+ let capped = false;
2271
+ const walk = (d) => {
2272
+ if (capped || files >= DIR_SCAN_MAX_FILES) {
2273
+ capped = true;
2274
+ return;
2275
+ }
2276
+ let names = [];
2277
+ try {
2278
+ names = readdirSync(d);
2279
+ } catch {
2280
+ return;
2281
+ }
2282
+ for (const name of names) {
2283
+ if (files >= DIR_SCAN_MAX_FILES) {
2284
+ capped = true;
2285
+ return;
2286
+ }
2287
+ const abs = join(d, name);
2288
+ let isDir = false;
2289
+ let size = 0;
2290
+ try {
2291
+ const st = statSync(abs);
2292
+ isDir = st.isDirectory();
2293
+ size = st.size;
2294
+ } catch {
2295
+ continue;
2296
+ }
2297
+ if (isDir) walk(abs);
2298
+ else {
2299
+ files++;
2300
+ bytes += size;
2301
+ }
2302
+ }
2303
+ };
2304
+ walk(dir);
2305
+ return {
2306
+ files,
2307
+ bytes,
2308
+ capped
2309
+ };
2310
+ }
2311
+ /** 按 UTF-8 **字节序**比较两个相对路径(规则②;不是 locale 比较、不是 mtime) */
2312
+ function byByteOrder(a, b) {
2313
+ return Buffer.compare(a.key, b.key);
2314
+ }
2315
+ /**
2316
+ * 千分位分组(规则②的延伸:**不依赖 locale**)。
2317
+ * 为什么不用 `toLocaleString()`:它随宿主 locale 变 ⇒ 同一目录在不同机器上产出**不同的注入文本**
2318
+ * (本仓已有一条「locale 稳健性」的挂账,不再添一处)。
2319
+ */
2320
+ function groupThousands(n) {
2321
+ return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
2322
+ }
2323
+ /**
2324
+ * 取 markdown 首个 `#` 标题作摘要(规则④)。
2325
+ *
2326
+ * 细节(都由"可复现"推导):
2327
+ * · **跳过 YAML frontmatter**(`---` 开头到下一 `---`)—— 否则题名行会被当成正文标题;
2328
+ * · 只读文件**头部** `HEAD_BYTES`(一行标题不值得读整个大文件);
2329
+ * · 剥掉行内记号(`*` `` ` `` `_` `#`)与首尾空白,压平内部连续空白;
2330
+ * · 取不到 ⇒ `''`(**留空,不编** —— 编一个摘要等于让索引说假话);
2331
+ * · 超长截断时**留省略号**(与规则③同一精神:截断要看得见)。
2332
+ */
2333
+ function firstHeading(file) {
2334
+ let head;
2335
+ try {
2336
+ head = readFileSync(file, "utf-8").slice(0, HEAD_BYTES);
2337
+ } catch {
2338
+ return "";
2339
+ }
2340
+ const lines = head.split(/\r?\n/);
2341
+ let start = 0;
2342
+ if (lines[0]?.trim() === "---") {
2343
+ for (let i = 1; i < lines.length; i++) if (lines[i]?.trim() === "---") {
2344
+ start = i + 1;
2345
+ break;
2346
+ }
2347
+ }
2348
+ for (let i = start; i < lines.length; i++) {
2349
+ const m = /^#{1,6}\s+(.*)$/.exec(lines[i] ?? "");
2350
+ if (!m) continue;
2351
+ const clean = (m[1] ?? "").replace(/[*`_]/g, "").replace(/\s+/g, " ").trim();
2352
+ if (clean === "") continue;
2353
+ return clean.length > EXCERPT_MAX_CHARS ? `${clean.slice(0, 59)}…` : clean;
2354
+ }
2355
+ return "";
2356
+ }
2357
+ /** 收集候选(`references/` 一级条目 + 固定名本体),并按字节序排好 */
2358
+ function collectItems(skillDir) {
2359
+ const items = [];
2360
+ const add = (rel, abs, ontology) => {
2361
+ let bytes;
2362
+ try {
2363
+ bytes = statSync(abs).size;
2364
+ } catch {
2365
+ return;
2366
+ }
2367
+ items.push({
2368
+ rel,
2369
+ kind: "file",
2370
+ bytes,
2371
+ excerpt: rel.endsWith(".md") ? firstHeading(abs) : "",
2372
+ ontology,
2373
+ key: Buffer.from(rel, "utf-8")
2374
+ });
2375
+ };
2376
+ const refDir = join(skillDir, REFERENCE_DIR);
2377
+ if (existsSync(refDir)) {
2378
+ let names = [];
2379
+ try {
2380
+ names = readdirSync(refDir).sort();
2381
+ } catch {
2382
+ names = [];
2383
+ }
2384
+ for (const name of names) {
2385
+ const abs = join(refDir, name);
2386
+ let isDir = false;
2387
+ try {
2388
+ isDir = statSync(abs).isDirectory();
2389
+ } catch {
2390
+ continue;
2391
+ }
2392
+ const rel = `${REFERENCE_DIR}/${name}`;
2393
+ if (isDir) {
2394
+ const { files, bytes, capped } = scanDirStats(abs);
2395
+ items.push({
2396
+ rel,
2397
+ kind: "dir",
2398
+ bytes,
2399
+ excerpt: `子目录 · ${capped ? `≥${groupThousands(files)}` : groupThousands(files)} 个文件`,
2400
+ ontology: false,
2401
+ key: Buffer.from(rel, "utf-8")
2402
+ });
2403
+ continue;
2404
+ }
2405
+ add(rel, abs, false);
2406
+ }
2407
+ }
2408
+ const ontoAbs = join(skillDir, SKILL_ONTOLOGY_FILE);
2409
+ if (existsSync(ontoAbs)) add(SKILL_ONTOLOGY_FILE, ontoAbs, true);
2410
+ items.sort(byByteOrder);
2411
+ return items;
2412
+ }
2413
+ /**
2414
+ * 生成第二层索引正文。
2415
+ *
2416
+ * @param skillDir skill 目录绝对路径
2417
+ * @param options 上限覆盖(缺省用本模块常量)
2418
+ * @returns 索引正文;**无可列项 ⇒ `''`**(规则⑥的一般化:`references/` 与 `skill.ttl` 都没有
2419
+ * ⇒ 整块不产出 —— 空标题是噪声)。调用方按"空串不产出该块"处理。
2420
+ */
2421
+ function buildResourceIndex(skillDir, options = {}) {
2422
+ const maxItems = options.maxItems ?? 40;
2423
+ const maxChars = options.maxChars ?? 4096;
2424
+ const items = collectItems(skillDir);
2425
+ if (items.length === 0) return "";
2426
+ const modeled = items.some((i) => i.ontology);
2427
+ const shown = items.slice(0, maxItems);
2428
+ const hidden = items.length - shown.length;
2429
+ const lines = ["### 本 skill 的参考件(第二层 · 按需读取)", "> 这里只有**清单**,没有正文:需要时用 `read` 打开下列路径(相对本 skill 目录)。"];
2430
+ if (modeled) lines.push("> 🧬 **本 skill 已建模**:文字内容以 `skill.ttl` 为总约束。");
2431
+ lines.push("", "| 文件 | 摘要 | 字节 |", "|---|---|---|");
2432
+ for (const item of shown) {
2433
+ const label = item.kind === "dir" ? `${item.rel}/` : item.rel;
2434
+ const excerpt = item.ontology ? "🧬 本 skill 的本体约束(已建模)" : item.excerpt;
2435
+ lines.push(`| ${label} | ${excerpt} | ${groupThousands(item.bytes)} |`);
2436
+ }
2437
+ if (hidden > 0) lines.push("", `…**还有 ${hidden} 项未列出**(本层单次上限 ${maxItems} 项)。`);
2438
+ const body = lines.join("\n");
2439
+ if (body.length <= maxChars) return body;
2440
+ return `${body.slice(0, maxChars)}\n…(第二层索引已按 ${groupThousands(maxChars)} 字符上限截断,原文 ${groupThousands(body.length)} 字符)`;
2441
+ }
2442
+ //#endregion
2218
2443
  //#region src/trajectory-skills.ts
2219
2444
  /**
2220
2445
  * trajectory-skills.ts — 轨迹 skill 声明解析 + 注入内容装配(纯逻辑,零 DSH 依赖)
@@ -2404,7 +2629,9 @@ function buildTrajectorySkillsSection(root, mdPath, maxChars = TRAJECTORY_SKILLS
2404
2629
  continue;
2405
2630
  }
2406
2631
  try {
2407
- blocks.push(`=== skill: ${name} ===\n${readFileSync(file, "utf-8")}`);
2632
+ const index = buildResourceIndex(dirname(file));
2633
+ const body = readFileSync(file, "utf-8");
2634
+ blocks.push(`=== skill: ${name} ===\n${index ? `${body}\n\n${index}` : body}`);
2408
2635
  } catch {
2409
2636
  blocks.push(missingNotice(name));
2410
2637
  }
@@ -3150,6 +3377,7 @@ function toolsBlock() {
3150
3377
  " praxis — actionable theory injection: praxis (index) / praxis eap / praxis neat / praxis cce / praxis ontology",
3151
3378
  " ontology — ontology engineering ENGINE (executable): init (write template) / check (M1-M10 structural; level=strict adds L1-L5 hints) / explain (summary) / verify (V1-V4 runtime compliance) / review (semantic-review prompt) / compliance (axiom-vs-skill checklist). Sibling of `praxis ontology` — that one is the METHOD TEXT, this one RUNS it. path must stay inside the container; the receipt carries the engine exit code (0/1/2/4) and the engine text verbatim, so read it rather than assuming success",
3152
3379
  " handyman — delegate work to a worker agent on a CCC-whitelisted model; mode=\"foreground\" (default) = child agent(s) run once and return their final text (jobs=[...] fans out in parallel, cap 10, per-job model); mode=\"background\" = loop-validated worker (completion code + round cap + restart + progress file), jobs=[] orchestrates parallel work",
3380
+ " serenity-skill-creator — creator for serenity-skills (this container's enhanced skill standard): guide (authoring/optimization method — read first) / create <name> (scaffold SKILL.md + skill.ttl + references/, base-chain import path computed) / check <name> (A1 structural + A2 base-chain; three states: modeled-ok / plain-skill / modeled-but-check-could-not-run) / review <name> (C tier: axiom-vs-text compliance question list, half-mechanical) / index <name> (preview the second-layer resource list the model will see) / status (which skills are modeled). A skill is \"modeled\" iff it has a file named exactly skill.ttl next to SKILL.md; a plain skill is NOT an anomaly. Only `create` writes, and it refuses an existing directory",
3153
3381
  " localstore — ACC local credential/config storage (CCC-root localstore.json; git policy localstore.gitTrack default deny)",
3154
3382
  " container_admin — container administration (the maintenance bay): role (Skiff roles: guide/validate/apply/list) / msm (register/deregister/check/guide/catalog/ccc-config) / config (view)",
3155
3383
  "",
@@ -3412,11 +3640,19 @@ function localstoreBlock(root) {
3412
3640
  ""
3413
3641
  ]).join("\n");
3414
3642
  }
3415
- /** 4) SKILL.md 全文:该 CCC 顶层入口 skill 原文(对齐 osp:原文直推,无包裹头;仅过滤治理内容) */
3643
+ /** 4) SKILL.md 全文:该 CCC 顶层入口 skill 原文(对齐 osp:原文直推,无包裹头;仅过滤治理内容)
3644
+ * + 🆕 **第二层披露树**(serenity-skill P1,设计 §4.2):每个入口 skill 后附**参考件清单**
3645
+ * (只有文件名与一行标题,**不含正文** —— 正文留给模型按需 `read`)。
3646
+ * 索引为空(该 skill 无 `references/` 也无 `skill.ttl`)⇒ **不产出该块**(空标题是噪声)。 */
3416
3647
  function entrySkillSectionText(root) {
3417
3648
  const skills = findEntrySkills(root);
3418
3649
  if (skills.length === 0) return "";
3419
- return skills.map((s) => sanitizeSkillContent(s.content)).filter((c) => c.length > 0).join("\n\n");
3650
+ return skills.map((s) => {
3651
+ const body = sanitizeSkillContent(s.content);
3652
+ if (body.length === 0) return "";
3653
+ const index = buildResourceIndex(s.dir);
3654
+ return index ? `${body}\n\n${index}` : body;
3655
+ }).filter((c) => c.length > 0).join("\n\n");
3420
3656
  }
3421
3657
  /** 活跃会话解析:按 dsh 会话 scope 读标记(隔离;不回退旧全局标记) */
3422
3658
  function resolveActiveSessionInfo(root, scope) {
@@ -4144,7 +4380,7 @@ function skiffMsmGate(root, sessionId, action, name) {
4144
4380
  function agentSessionId$1(exec) {
4145
4381
  return exec.agent?.session?.id ?? "";
4146
4382
  }
4147
- function renderText$7(value) {
4383
+ function renderText$8(value) {
4148
4384
  return [{
4149
4385
  type: "text",
4150
4386
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -4194,7 +4430,7 @@ const msmTool = defineTool({
4194
4430
  },
4195
4431
  output: {
4196
4432
  schema: { type: "json" },
4197
- render: (args, value) => renderText$7(value)
4433
+ render: (args, value) => renderText$8(value)
4198
4434
  },
4199
4435
  async execute(args, exec) {
4200
4436
  const root = cccRootForExec(exec);
@@ -4668,7 +4904,7 @@ const PRAXIS_INDEX = `praxis — 可实践理论注入(认知框架,按需
4668
4904
  praxis ontology(本体论工程方法群——触发/编码/四组判据 M·L·D·V/OEPE 循环/公理/反模式)
4669
4905
  无参返回本目录;详情以对应框架原文为准(单一真相源)。
4670
4906
  `;
4671
- function renderText$6(value) {
4907
+ function renderText$7(value) {
4672
4908
  return [{
4673
4909
  type: "text",
4674
4910
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -4689,7 +4925,7 @@ const praxisTool = defineTool({
4689
4925
  } },
4690
4926
  output: {
4691
4927
  schema: { type: "string" },
4692
- render: (_args, value) => renderText$6(value)
4928
+ render: (_args, value) => renderText$7(value)
4693
4929
  },
4694
4930
  async execute(args) {
4695
4931
  if (args.section === "eap") return EAP_CONTENT;
@@ -7373,7 +7609,7 @@ const LEVELS_BY_COMMAND = {
7373
7609
  "full"
7374
7610
  ]
7375
7611
  };
7376
- function renderText$5(value) {
7612
+ function renderText$6(value) {
7377
7613
  return [{
7378
7614
  type: "text",
7379
7615
  text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
@@ -7434,7 +7670,7 @@ function createOntologyTool() {
7434
7670
  },
7435
7671
  output: {
7436
7672
  schema: { type: "string" },
7437
- render: (_args, value) => renderText$5(value)
7673
+ render: (_args, value) => renderText$6(value)
7438
7674
  },
7439
7675
  async execute(args, exec) {
7440
7676
  const root = cccRootForExec(exec);
@@ -7483,6 +7719,393 @@ function createOntologyTool() {
7483
7719
  });
7484
7720
  }
7485
7721
  //#endregion
7722
+ //#region src/skills/skill-ontology.ts
7723
+ /**
7724
+ * skill-ontology.ts — **本体预留口**的发现与检查(纯逻辑,零 DSH 依赖)
7725
+ *
7726
+ * 设计真相源:`docs/serenity-skill-design.md` §4.1(四个动作)/§5.1-5.2(固定名与骨架)/§6(J1~J3)。
7727
+ * 一句话:**一个 skill 若完成了本体建模,就在自己目录放固定名 `skill.ttl`**;
7728
+ * 本模块回答三件事:**有没有**(发现)/**合不合法**(A 档)/**引用能不能兑现**(B 档)。
7729
+ *
7730
+ * 🔴 只认**固定名** `<skillDir>/skill.ttl`(owner 裁决 Q6:静默、CCC 自行修正):
7731
+ * `references/*-domain.ttl` 这类**旧名一律当普通参考件** —— 不特殊对待、**不告警**、**不代读**。
7732
+ * 🔴 「无 `skill.ttl`」**不是错误也不是告警**(裁决 Q6 / 判据 J9):
7733
+ * 那是**普通 skill**,全程无声 —— 增强必须对普通 skill 完全向后兼容。
7734
+ *
7735
+ * ── 两档判据(**两档都锚在引擎自己的输出上**,我们不另写一遍 Turtle 解析)──────────
7736
+ * **A1 结构合法**:`check --level strict` exit 0(引擎的 M1–M10)
7737
+ * **A2 链上基础链**:`explain` exit 0 **且**报告里有 `Imports:` 行
7738
+ *
7739
+ * 🔴 **为什么必须有 A2(这是本模块存在的理由,且是实测逼出来的)**:
7740
+ * **A1 绿 ≠ 链上了**。实测对照(同一天、同一引擎):
7741
+ * · `home-rhetoric/references/home-rhetoric-domain.ttl`(写的是 `sr:imports`)
7742
+ * ⇒ `check --level strict` = **✓ exit 0 · All checks passed**,而 `explain` **没有 `Imports:` 行**;
7743
+ * · 注册表(写的是 `owl:imports`)⇒ `explain` **打印 `Imports: ccc-ontology.ttl`**(探针不瞎)。
7744
+ * ⇒ **「检查全绿」与「链上了」是两件事**;只看 A1 就会把 14 份没链上的本体判成合格。
7745
+ *
7746
+ * 🟢 **A3(引用可兑现)**:**本模块刻意不做** —— 引擎已经做了,而且是**响亮的**。实测两条:
7747
+ * · `check`:import 目标不存在 ⇒ **`ERR [M10] Ontology file not found: <abs>`**、**exit 1**;
7748
+ * · `explain`:同一个文件 ⇒ **exit 2 · `ONTO_FILE_NOT_FOUND`**。
7749
+ * ⇒ 再写一遍就是**同一件事的第二实现**(本仓的老账:"先问谁已经做了")。
7750
+ * 🔴 但**起因值得记**:我原本以为引擎对缺失 import 是**静默**的(照源码 1687 行那句 "Fall back to
7751
+ * loading just this file (no imports)" 推断)—— **实测把这条推断打假了**(两条路都响亮报错)。
7752
+ * ⇒ 又一次「**照源码的意图抄 ≠ 引擎实际怎么行为**」(D119 同族)。
7753
+ */
7754
+ /** 从引擎 `explain` 的报告里取 `Imports:` 行(**锚在引擎自己的输出上**,见文件头 A2 注) */
7755
+ function parseImports(explainStdout) {
7756
+ const out = [];
7757
+ for (const line of explainStdout.split("\n")) {
7758
+ const m = /^Imports:\s*(.+)$/.exec(line.trim());
7759
+ if (!m) continue;
7760
+ for (const part of (m[1] ?? "").split(",")) {
7761
+ const t = part.trim();
7762
+ if (t !== "") out.push(t);
7763
+ }
7764
+ }
7765
+ return out;
7766
+ }
7767
+ /**
7768
+ * 检查一个 skill 的预留口。
7769
+ *
7770
+ * @param skillDir skill 目录(绝对路径)
7771
+ * @param opts.root CCC 根(引擎基准;**必须是 CCC 根**,否则引擎找不到容器级资产)
7772
+ * @returns 三态 + 逐档判据
7773
+ */
7774
+ function inspectSkillOntology(skillDir, opts) {
7775
+ const abs = resolve(skillDir, SKILL_ONTOLOGY_FILE);
7776
+ if (!existsSync(abs)) return {
7777
+ state: "none",
7778
+ rel: "",
7779
+ modeled: false,
7780
+ checks: [],
7781
+ imports: []
7782
+ };
7783
+ const relOf = (p) => p.startsWith(opts.root) ? p.slice(opts.root.length).replace(/^[/\\]/, "") : p;
7784
+ const rel = relOf(abs);
7785
+ const checks = [];
7786
+ const c1 = runOntologyCli([
7787
+ "check",
7788
+ abs,
7789
+ "--level",
7790
+ "strict"
7791
+ ], { cwd: opts.root });
7792
+ checks.push({
7793
+ id: "A1",
7794
+ ok: c1.code === 0,
7795
+ detail: c1.code === 0 ? "strict 结构校验通过" : c1.code === 1 ? "结构校验未通过(引擎 ERR 行见报告)" : c1.code === 2 ? `解析失败/读不到文件(code 2)${c1.stderr.trim() !== "" ? `:${c1.stderr.trim().split("\n")[0]}` : ""}` : `引擎内部错误(code ${c1.code})`
7796
+ });
7797
+ const e1 = runOntologyCli(["explain", abs], { cwd: opts.root });
7798
+ const imports = e1.code === 0 ? parseImports(e1.stdout) : [];
7799
+ const explainFirstErr = e1.stderr.trim().split("\n").find((l) => l.trim() !== "") ?? "";
7800
+ checks.push({
7801
+ id: "A2",
7802
+ ok: e1.code === 0 && imports.length > 0,
7803
+ detail: e1.code !== 0 ? `**探测没跑成**(引擎 explain exit ${e1.code})—— ${explainFirstErr !== "" ? explainFirstErr : "引擎未给原因"}` : imports.length > 0 ? `已链上基础链:${imports.join(", ")}` : "**未声明 `owl:imports`** —— 与基础链**没链上**(引擎只解析 `owl:imports`;写 `sr:imports` 不会被读 —— 这是实测过的静默形态,且 `check` 对它会**照样报全绿**)"
7804
+ });
7805
+ return {
7806
+ state: checks.every((c) => c.ok) ? "ok" : "error",
7807
+ rel,
7808
+ modeled: true,
7809
+ checks,
7810
+ imports
7811
+ };
7812
+ }
7813
+ /**
7814
+ * 渲染三态报告(**三态必须互可分辨** —— 判据 J3;措辞刻意不许糊)。
7815
+ *
7816
+ * 为什么单独一个函数:`check` 的呈现(P3 的工具)与测试看的是**同一段文本**,
7817
+ * 两处各写一遍必然漂移(本仓"路径写两遍必漂"的既有教训)。
7818
+ */
7819
+ function formatSkillOntologyInspection(skillName, insp) {
7820
+ const lines = [insp.state === "ok" ? `✅ ${skillName}:**已建模且全部通过**` : insp.state === "none" ? `⬜ ${skillName}:**普通 skill(未建模)**—— 无 \`${SKILL_ONTOLOGY_FILE}\`` : `❌ ${skillName}:**建模了但检查没跑成**`];
7821
+ if (insp.state === "none") {
7822
+ lines.push(" (**这不是\"检查通过\",是\"没有这项检查\"**;也不是异常 —— 增强对普通 skill 完全向后兼容。)");
7823
+ return lines.join("\n");
7824
+ }
7825
+ lines.push(` ${insp.rel}`);
7826
+ for (const c of insp.checks) lines.push(` ${c.ok ? "✅" : "❌"} ${c.id} ${c.detail}`);
7827
+ return lines.join("\n");
7828
+ }
7829
+ //#endregion
7830
+ //#region src/tools/skill-creator.ts
7831
+ /**
7832
+ * skill-creator.ts(工具面)— `serenity-skill-creator`:**增强型 skill(serenity-skill)的 creator**
7833
+ *
7834
+ * 设计真相源:`docs/serenity-skill-design.md` §4.3(六个子命令)/§5(接口)/§6(判据 J9/J10)。
7835
+ * 来源:owner 2026-10-11 令 —— 「扩展 skill 相关 tool,将**基于本体论建模的 skill 编写和优化方法**进行交代,
7836
+ * 其实也就是我们这种增强型 skill 的 creator」,且**点名**要一个专门的工具、**名字由他给**。
7837
+ *
7838
+ * 🔴 **三段分工(别混)**:
7839
+ * · **方法文本** = 本文件的 `SKILL_CREATOR_GUIDE`(`guide` 子命令印它)—— 给人/模型读"怎么做"。
7840
+ * · **机械判据** = `src/skills/skill-ontology.ts`(P2)与 `src/skills/resource-index.ts`(P1)——
7841
+ * 本工具**只做转发与呈现,绝不重写一遍检查**(同一件事的第二实现是本仓的老账)。
7842
+ * · **语义档 C** = 引擎的 `compliance`(半机械:出逐条可回答问题清单,交 LLM/人在环回答)。
7843
+ *
7844
+ * 🔴 **J9:未建模 ≠ 异常。** 没有 `skill.ttl` 的**普通 skill** 全程无声 —— 本工具任何子命令都
7845
+ * 不许把它渲染成错误或告警,只是"少一项能力"。
7846
+ *
7847
+ * 🔴 **写文件只发生在 `create`**,且**目标已存在一律拒**(不许静默覆盖别人目录)。
7848
+ */
7849
+ const ACTIONS$2 = [
7850
+ "guide",
7851
+ "create",
7852
+ "check",
7853
+ "review",
7854
+ "index",
7855
+ "status"
7856
+ ];
7857
+ /** 基础链(skill.ttl 必须链上的那个文件)在**顶层 skill** 的 references/ 下 */
7858
+ const BASE_CHAIN_FILE = "serenity-skills-ontology.ttl";
7859
+ /** 缺省顶层 skill 名(与引擎的 TOP_SKILL 缺省一致;`create` 会优先读 `.serenity`) */
7860
+ const DEFAULT_TOP_SKILL = "home-serenity";
7861
+ /**
7862
+ * 《serenity-skill 编写与优化方法》(`guide` 印它)。
7863
+ *
7864
+ * 边界(D23):**本文只是方法**;判据在代码里(P1/P2),语义审查在引擎的 `compliance` 里 ——
7865
+ * 三者**不许互相抄**(同一件事写两遍必然漂移)。
7866
+ */
7867
+ const SKILL_CREATOR_GUIDE = `# serenity-skill —— 增强型 skill 的编写与优化方法
7868
+
7869
+ ## 何时该建模,何时不该
7870
+
7871
+ **该建模**:该领域有**稳定的类型与关系**(谁是谁、一对多、什么必须先于什么),且这些结构**会反复被引用**
7872
+ (排错、审查、写文档都要回去看它)—— 典型如"一个 skill 管哪些资产、资产之间什么约束"。
7873
+
7874
+ **不该建模**(**不建模不是缺陷**):
7875
+ · 纯操作规程("先跑 A 再跑 B")—— 那是 SKILL.md 的正文,给它套本体只是换个地方写散文;
7876
+ · 一次性/短命的事;
7877
+ · 你自己都还说不清领域里到底有哪几类东西时 —— **先写散文,等结构浮现再建模**(建模不是入场券)。
7878
+
7879
+ ## 三个动作(做完即"已建模")
7880
+
7881
+ 1. 在 skill 目录放**固定名** \`skill.ttl\`(与 \`SKILL.md\` 并列)—— 这是**唯一**被认的名字。
7882
+ ⚠️ 旧写法 \`references/<name>-domain.ttl\` **不被认**(静默当普通参考件):不是报错,是**没有人会看它**。
7883
+ 2. \`skill.ttl\` **必须链上基础链**,形式是 \`owl:imports <相对路径>\`:
7884
+ \`\`\`turtle
7885
+ :onto a owl:Ontology ;
7886
+ sr:name "<skill-name>" ;
7887
+ owl:versionInfo "0.1.0" ;
7888
+ owl:imports <../home-serenity/references/serenity-skills-ontology.ttl> .
7889
+ \`\`\`
7890
+ 🔴 **相对路径由 \`create\` 算**(从本 skill 目录指向顶层 skill 的 references/),**不要人肉推**。
7891
+ 🔴 引擎**只解析 \`owl:imports\`**;写 \`sr:imports\` **不会被读**,而且 \`check\` 对它**照样报全绿** ——
7892
+ 所以别只看 \`check\` 绿就以为链上了(这正是本工具 \`check\` 要同时跑 A2 的原因)。
7893
+ 3. 让 \`check\` 全绿(A1 结构合法 + A2 链上基础链)。
7894
+
7895
+ ## 两层披露树的写法习惯(影响模型能不能找得到)
7896
+
7897
+ · \`references/\` 里的文件名**就是目录**:前缀数字(\`00-\`…\`90-\`)给稳定次序,名字**写成能读懂的一句**。
7898
+ · 每个参考文件的**首个 \`#\` 标题**会被当作索引里的摘要 ⇒ **给每个 .md 写一个说人话的首标题**(别写文件名复读)。
7899
+ · 索引**只列不读**:正文永远留给按需 \`read\` ⇒ 别把大段内容塞进 SKILL.md(它每请求都注入)。
7900
+ · \`references/\` 下的**子目录**会被列成一行(名字+文件数),**不展开** ⇒ 目录名要起得让人知道里面是什么。
7901
+
7902
+ ## 从领域散文到 ttl(具体步骤)
7903
+
7904
+ 1. 把 SKILL.md 里的名词圈出来 → 挑出**反复出现、有身份**的那些 = 候选类型;
7905
+ 2. 给每个类型写:\`rdfs:label\` + \`sr:rigidity\`(\`rigid\` 身份固定/\`anti-rigid\` 会变)+ 身份属性(\`sr:identity\`);
7906
+ 3. 把动词圈出来 → 有方向、有基数的 = 关系(\`rdfs:domain\`/\`rdfs:range\` + \`sr:cardinality\`);
7907
+ 4. 把"永远成立的话"写成公理(\`sr:axiom\`)—— **只写真能判真假的**,别写口号;
7908
+ 5. 补 \`sr:source\`/来源(对外部主张)。
7909
+ **方法正文不在这里重复**:见 \`praxis ontology\`(本体工程方法群:四组判据 M1–M10/L1–L5/D1–D8/V1–V4、OEPE 回路、反模式)。
7910
+
7911
+ ## 优化回路(改完之后再走一遍)
7912
+
7913
+ \`\`\`
7914
+ serenity-skill-creator index <skill> ← 先看模型到底会看到什么(露没露、顺序对不对)
7915
+ serenity-skill-creator check <skill> ← A1 结构 + A2 链上(机械,必须全绿)
7916
+ serenity-skill-creator review <skill> ← C 档:文字与 ttl 逐条的合规问题清单(要人在环答)
7917
+ (改 SKILL.md / skill.ttl / references)
7918
+ ↑__________________________ 回到第一步
7919
+ \`\`\`
7920
+
7921
+ **判据**:改完**必须重跑** \`check\` —— 本仓的教训是"检查没跑"和"检查通过"在输出上必须分得开;
7922
+ \`review\` 出的是**问题清单**(半机械、要人回答),**不是**通过/不通过。
7923
+ `;
7924
+ function renderText$5(value) {
7925
+ return [{
7926
+ type: "text",
7927
+ text: typeof value === "string" ? value : JSON.stringify(value, null, 2)
7928
+ }];
7929
+ }
7930
+ /** 顶层 skill 名:优先 `.serenity` 记号文件(CCC 的权威语义),否则缺省 */
7931
+ function readTopSkill(root) {
7932
+ try {
7933
+ const marker = join(root, ".serenity");
7934
+ if (existsSync(marker)) {
7935
+ const name = readFileSync(marker, "utf-8").trim();
7936
+ if (name !== "") return name;
7937
+ }
7938
+ } catch {}
7939
+ return DEFAULT_TOP_SKILL;
7940
+ }
7941
+ /** skill 目录(容器内相对路径 → 绝对);名字先过安全门 */
7942
+ function skillDirOf(root, name) {
7943
+ if (!isSafeSkillName(name)) throw new Error(`serenity-skill-creator: 非法 skill 名 "${name}"(只允许简单目录名,不许含 / 或 ..)`);
7944
+ return resolveInside(root, join(".opencode", "skills", name));
7945
+ }
7946
+ /** 从 skill 目录算到基础链的相对路径(**由本工具算,不靠人肉推**) */
7947
+ function baseChainImport(root, skillDir) {
7948
+ const top = readTopSkill(root);
7949
+ const base = join(root, ".opencode", "skills", top, "references", BASE_CHAIN_FILE);
7950
+ return relative(skillDir, base).split("\\").join("/");
7951
+ }
7952
+ /** 列出 CCC 的全部 skill 目录名(有 SKILL.md 的一级目录) */
7953
+ function listSkillNames(root) {
7954
+ const skillsRoot = join(root, ".opencode", "skills");
7955
+ if (!existsSync(skillsRoot)) return [];
7956
+ return readdirSync(skillsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && existsSync(join(skillsRoot, e.name, "SKILL.md"))).map((e) => e.name).sort();
7957
+ }
7958
+ /** `create` 的脚手架内容 */
7959
+ function scaffoldFiles(name, importPath) {
7960
+ return {
7961
+ "SKILL.md": `---
7962
+ name: ${name}
7963
+ description: (一句话说清这个 skill 管什么、什么时候该加载它)
7964
+ ---
7965
+
7966
+ # Skill: ${name}
7967
+
7968
+ ## 用途
7969
+
7970
+ (这个领域是什么、管什么、不管什么)
7971
+
7972
+ ## 触发条件/何时加载
7973
+
7974
+ (什么情况下该加载本 skill)
7975
+
7976
+ ## 领域结构
7977
+
7978
+ (有本体时:本领域的类型与关系在这里只做**指路**,真相源是同级 \`${SKILL_ONTOLOGY_FILE}\`)
7979
+
7980
+ ## 相关技能
7981
+
7982
+ (上下游 skill)
7983
+ `,
7984
+ [SKILL_ONTOLOGY_FILE]: `@prefix sr: <http://home-serenity/ontology#> .
7985
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
7986
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7987
+ @prefix : <http://home-serenity/ontology/${name}#> .
7988
+
7989
+ # 令 1.3:**必须**链上基础链。形式 = owl:imports <相对路径>(相对本文件所在目录解析)。
7990
+ # 🔴 本行由 serenity-skill-creator 的 create 计算,别改成 sr:imports、也别手推路径。
7991
+ :onto a owl:Ontology ;
7992
+ sr:name "${name}" ;
7993
+ owl:versionInfo "0.1.0" ;
7994
+ owl:imports <${importPath}> .
7995
+
7996
+ # ── 类型(把领域里"有身份、会反复被提到"的名词放这里)──────────────
7997
+ :Thing a owl:Class ;
7998
+ rdfs:label "Thing" ;
7999
+ rdfs:comment "(一句话说清这类东西是什么)" ;
8000
+ sr:rigidity sr:rigid ;
8001
+ sr:identity ("thingId") ;
8002
+ sr:property [ sr:name "thingId" ; sr:type "string" ; sr:cardinality "exactly 1" ] .
8003
+ `
8004
+ };
8005
+ }
8006
+ function createSkillCreatorTool() {
8007
+ return defineTool({
8008
+ name: "serenity-skill-creator",
8009
+ description: "Creator for serenity-skills (this container's enhanced skill standard): write and optimize ontology-modeled skills. Subcommands: guide (the authoring/optimization method — read this first when creating or reworking a skill) / create <name> (scaffold a skill dir: SKILL.md + skill.ttl + references/, with the base-chain import path computed) / check <name> (A1 structural + A2 base-chain chained; three states — modeled-ok / plain-skill / modeled-but-check-could-not-run) / review <name> (C tier: axiom-vs-text compliance question list, half-mechanical — a human/LLM answers it) / index <name> (preview the second-layer resource list the model will actually see) / status (whole container: which skills are modeled, which are plain). A skill is \"modeled\" iff it has a file named exactly skill.ttl next to SKILL.md; a plain skill (no skill.ttl) is NOT an anomaly — it simply has no ontology. Only `create` writes, and it refuses to touch an existing skill directory.",
8010
+ parameters: {
8011
+ action: {
8012
+ type: "string",
8013
+ enum: [...ACTIONS$2],
8014
+ description: "guide / create / check / review / index / status."
8015
+ },
8016
+ name: {
8017
+ type: "string",
8018
+ description: "Skill name (required for create/check/review/index; must be a simple directory name)."
8019
+ }
8020
+ },
8021
+ output: {
8022
+ schema: { type: "string" },
8023
+ render: (_args, value) => renderText$5(value)
8024
+ },
8025
+ async execute(args, exec) {
8026
+ const root = cccRootForExec(exec);
8027
+ if (!root) throw new Error(NO_CCC_FROM_AGENT_CWD);
8028
+ const action = typeof args.action === "string" ? args.action.trim() : "";
8029
+ if (!ACTIONS$2.includes(action)) throw new Error(`serenity-skill-creator: \`action\` must be one of ${ACTIONS$2.join(" / ")} (got: ${action === "" ? "(missing)" : action})`);
8030
+ const name = typeof args.name === "string" ? args.name.trim() : "";
8031
+ const needName = () => {
8032
+ if (name === "") throw new Error(`serenity-skill-creator: \`name\` is required for \`${action}\``);
8033
+ return name;
8034
+ };
8035
+ if (action === "guide") return SKILL_CREATOR_GUIDE;
8036
+ if (action === "status") {
8037
+ const names = listSkillNames(root);
8038
+ if (names.length === 0) return `(\`.opencode/skills\` 下没有带 SKILL.md 的 skill)`;
8039
+ const modeled = [];
8040
+ const plain = [];
8041
+ const broken = [];
8042
+ for (const n of names) {
8043
+ const dir = join(root, ".opencode", "skills", n);
8044
+ if (!existsSync(join(dir, "skill.ttl"))) {
8045
+ plain.push(n);
8046
+ continue;
8047
+ }
8048
+ const insp = inspectSkillOntology(dir, { root });
8049
+ if (insp.state === "ok") modeled.push(n);
8050
+ else broken.push(formatSkillOntologyInspection(n, insp));
8051
+ }
8052
+ return [
8053
+ `serenity-skill 一览(顶层 skill = ${readTopSkill(root)})`,
8054
+ `\n🧬 **已建模且通过** ${modeled.length} 个` + (modeled.length > 0 ? `:${modeled.join(", ")}` : ""),
8055
+ `\n⬜ **普通 skill(未建模)** ${plain.length} 个`,
8056
+ " ⚠️ 这**不是异常**,只是少一项能力(J9:增强对普通 skill 完全向后兼容)。",
8057
+ broken.length > 0 ? `\n❌ **建模了但检查没跑成** ${broken.length} 个:\n${broken.join("\n")}` : ""
8058
+ ].filter((s) => s !== "").join("\n");
8059
+ }
8060
+ const dir = skillDirOf(root, needName());
8061
+ if (action === "create") {
8062
+ if (existsSync(dir)) throw new Error(`serenity-skill-creator: 目录已存在,拒绝覆盖 → ${relative(root, dir)}(改造既有 skill 请直接编辑它的 SKILL.md/skill.ttl;本工具不代劳,免得静默盖掉你的东西)`);
8063
+ const files = scaffoldFiles(name, baseChainImport(root, dir));
8064
+ mkdirSync(join(dir, REFERENCE_DIR), { recursive: true });
8065
+ for (const [rel, content] of Object.entries(files)) writeFileSync(join(dir, rel), content, "utf-8");
8066
+ return [
8067
+ `✅ 已生成 skill 脚手架:${relative(root, dir)}`,
8068
+ "",
8069
+ ...Object.keys(files).map((f) => ` · ${f}`),
8070
+ ` · ${REFERENCE_DIR}/(空目录)`,
8071
+ "",
8072
+ `🔗 skill.ttl 已链上基础链(相对路径由本工具算):\`${baseChainImport(root, dir)}\``,
8073
+ "👉 下一步:填 SKILL.md 的用途/触发条件 → 跑 `check` 让它全绿 → 需要时 `index` 看模型会看到什么。",
8074
+ " (方法论:`serenity-skill-creator guide`;本体工程方法群:`praxis ontology`。)"
8075
+ ].join("\n");
8076
+ }
8077
+ if (!existsSync(dir)) throw new Error(`serenity-skill-creator: 找不到 skill 目录 → ${relative(root, dir)}(名字拼错?)`);
8078
+ if (action === "index") {
8079
+ const idx = buildResourceIndex(dir);
8080
+ return idx === "" ? `${name}:**无参考件可列**(无 \`${REFERENCE_DIR}/\` 且无 \`${SKILL_ONTOLOGY_FILE}\`)⇒ 注入时**不产出**第二层块。` : `以下就是模型在提示词里**真正会看到**的第二层(只有清单,没有正文):\n\n${idx}`;
8081
+ }
8082
+ if (action === "check") {
8083
+ const insp = inspectSkillOntology(dir, { root });
8084
+ const body = formatSkillOntologyInspection(name, insp);
8085
+ if (insp.state === "none") return `${body}\n\n(要建模:\`serenity-skill-creator guide\` 看三个动作,或 \`create\` 生成骨架。)`;
8086
+ return insp.state === "ok" ? `${body}\n\n👉 机械档过了。语义档(文字是否真遵守模型/公理)走 \`review\`。` : `${body}\n\n👉 修完**重跑本命令**(本仓教训:「检查没跑」与「检查通过」必须分得开)。`;
8087
+ }
8088
+ const ttl = join(dir, SKILL_ONTOLOGY_FILE);
8089
+ if (!existsSync(ttl)) return formatSkillOntologyInspection(name, inspectSkillOntology(dir, { root })) + `\n\n(\`review\` 需要 \`${SKILL_ONTOLOGY_FILE}\`;普通 skill 没有可审的模型 —— 这不是错误。)`;
8090
+ const r = runOntologyCli([
8091
+ "compliance",
8092
+ ttl,
8093
+ "--skills",
8094
+ join(root, ".opencode", "skills")
8095
+ ], { cwd: root });
8096
+ return [
8097
+ `[review ${name}] 引擎 compliance ${r.code === 0 ? "✓" : "✗"} exit ${r.code}`,
8098
+ "",
8099
+ r.stdout.trim() !== "" ? r.stdout.trimEnd() : "(引擎无输出)",
8100
+ r.code !== 0 && r.stderr.trim() !== "" ? `\n${r.stderr.trimEnd()}` : "",
8101
+ "",
8102
+ "⚠️ 这是 **C 档(半机械)**:它给的是**逐条要回答的问题清单**,不是\"通过/不通过\"。",
8103
+ " 请**逐条回答**;发现文字与模型不符 ⇒ 改文字或改 ttl,然后重跑 `check`。"
8104
+ ].filter((s) => s !== "").join("\n");
8105
+ }
8106
+ });
8107
+ }
8108
+ //#endregion
7486
8109
  //#region src/handyman-preset-inherit.ts
7487
8110
  /**
7488
8111
  * 解析 handyman worker 从父 agent 继承的 preset,并组装创建 setup 钩子。
@@ -13990,6 +14613,7 @@ function registerOpencodeSkills(ctx) {
13990
14613
  const dir = candidate.locator;
13991
14614
  const skill = loadOpencodeSkill(dir);
13992
14615
  if (!skill) return void 0;
14616
+ const index = buildResourceIndex(dir);
13993
14617
  return {
13994
14618
  name: skill.meta.name,
13995
14619
  description: skill.meta.description || candidate.description,
@@ -13998,7 +14622,7 @@ function registerOpencodeSkills(ctx) {
13998
14622
  source: candidate.source,
13999
14623
  provider: OPENCODE_PROVIDER,
14000
14624
  resourceBase: candidate.resourceBase,
14001
- content: skill.content,
14625
+ content: index ? `${skill.content}\n\n${index}` : skill.content,
14002
14626
  path: skill.path
14003
14627
  };
14004
14628
  }
@@ -23616,6 +24240,7 @@ function apply(ctx, config) {
23616
24240
  ctx.tools.register(msmTool);
23617
24241
  ctx.tools.register(praxisTool);
23618
24242
  ctx.tools.register(createOntologyTool());
24243
+ ctx.tools.register(createSkillCreatorTool());
23619
24244
  ctx.tools.register(createHandymanTool(ctx));
23620
24245
  ctx.tools.register(localstoreTool);
23621
24246
  ctx.tools.register(createContainerAdminTool(ctx));
@@ -28,5 +28,11 @@ export declare function verifyToolConsistency(manifestPath: string, registeredTo
28
28
  * v1.52(**D121**,owner 2026-10-11 裁「引擎搬进 ACC」):新增 **`ontology`** —— 本体工程
29
29
  * **引擎**(原为 CCC 的 MSM `ontology-engineering`,逐字搬迁;见 `src/ontology/engine.ts` 文件头
30
30
  * 的三处改写)。它与 `praxis ontology`(**方法文本**)是**同一领域的两个面**:一个说、一个跑 ⇒ **13**。
31
+ *
32
+ * v1.54(**serenity-skill P3**,owner 2026-10-11 令「要一个 serenity-skill-creator 的 tool」):
33
+ * 新增 **`serenity-skill-creator`** —— 增强型 skill 标准(serenity-skill)的 creator:
34
+ * `guide`(方法文本)/`create`(脚手架;**基础链的相对路径由它算**)/`check`(调 P2 的 A1/A2)/
35
+ * `review`(C 档,调引擎 `compliance`)/`index`(调 P1 的生成器)/`status` ⇒ **14**。
36
+ * 同规格:**条件可见与否不影响这里列它**(本常量 = "插件**具备**的工具面")。
31
37
  */
32
- export declare const REGISTERED_TOOLS: readonly ["container_fs", "container_trajectory", "dashboard", "container_git", "msm", "praxis", "handyman", "localstore", "container_admin", "im-bridge", "acc-diag", "diagram", "ontology"];
38
+ export declare const REGISTERED_TOOLS: readonly ["container_fs", "container_trajectory", "dashboard", "container_git", "msm", "praxis", "handyman", "localstore", "container_admin", "im-bridge", "acc-diag", "diagram", "ontology", "serenity-skill-creator"];
package/lib/invariant.js CHANGED
@@ -49,6 +49,12 @@ function verifyToolConsistency(manifestPath, registeredTools) {
49
49
  * v1.52(**D121**,owner 2026-10-11 裁「引擎搬进 ACC」):新增 **`ontology`** —— 本体工程
50
50
  * **引擎**(原为 CCC 的 MSM `ontology-engineering`,逐字搬迁;见 `src/ontology/engine.ts` 文件头
51
51
  * 的三处改写)。它与 `praxis ontology`(**方法文本**)是**同一领域的两个面**:一个说、一个跑 ⇒ **13**。
52
+ *
53
+ * v1.54(**serenity-skill P3**,owner 2026-10-11 令「要一个 serenity-skill-creator 的 tool」):
54
+ * 新增 **`serenity-skill-creator`** —— 增强型 skill 标准(serenity-skill)的 creator:
55
+ * `guide`(方法文本)/`create`(脚手架;**基础链的相对路径由它算**)/`check`(调 P2 的 A1/A2)/
56
+ * `review`(C 档,调引擎 `compliance`)/`index`(调 P1 的生成器)/`status` ⇒ **14**。
57
+ * 同规格:**条件可见与否不影响这里列它**(本常量 = "插件**具备**的工具面")。
52
58
  */
53
59
  const REGISTERED_TOOLS = [
54
60
  "container_fs",
@@ -63,7 +69,8 @@ const REGISTERED_TOOLS = [
63
69
  "im-bridge",
64
70
  "acc-diag",
65
71
  "diagram",
66
- "ontology"
72
+ "ontology",
73
+ "serenity-skill-creator"
67
74
  ];
68
75
  //#endregion
69
76
  export { REGISTERED_TOOLS, verifyToolConsistency };
@@ -84,7 +84,10 @@ export declare function safeModeBlock(root: string): string;
84
84
  * 只限于该文件内(不外泄到其他文件/对话/日志)。
85
85
  */
86
86
  export declare function localstoreBlock(root: string): string;
87
- /** 4) SKILL.md 全文:该 CCC 顶层入口 skill 原文(对齐 osp:原文直推,无包裹头;仅过滤治理内容) */
87
+ /** 4) SKILL.md 全文:该 CCC 顶层入口 skill 原文(对齐 osp:原文直推,无包裹头;仅过滤治理内容)
88
+ * + 🆕 **第二层披露树**(serenity-skill P1,设计 §4.2):每个入口 skill 后附**参考件清单**
89
+ * (只有文件名与一行标题,**不含正文** —— 正文留给模型按需 `read`)。
90
+ * 索引为空(该 skill 无 `references/` 也无 `skill.ttl`)⇒ **不产出该块**(空标题是噪声)。 */
88
91
  export declare function entrySkillSectionText(root: string): string;
89
92
  /**
90
93
  * 5) Session 块:对齐 osp(活跃会话 + todowrite 首位约定);scope = dsh 会话维度。
@@ -0,0 +1,64 @@
1
+ /**
2
+ * resource-index.ts — **两层披露树的第二层**:skill 参考件索引(纯逻辑,零 DSH 依赖)
3
+ *
4
+ * 设计真相源:`docs/serenity-skill-design.md` §4.2(六条硬性规则)+ §5.1(目录布局)。
5
+ * 一句话:**把 skill 的参考件"有什么"告诉模型,但不把"是什么"塞进去** ——
6
+ * 正文留给第三层(模型按需 `read`)。
7
+ *
8
+ * 为什么由我们生成(R↓,读宿主取证):宿主 `@deepseek-ai/dsh-skill` 的 `renderSkillContent()`
9
+ * (`lib/index.js` L57-81)**已经**产出 `<skill_resources>` 块,并逐字写着
10
+ * "Load referenced resources only as needed." ⇒ **两层模型是宿主自己给的**;而
11
+ * `SkillSummary` / `SkillDefinition` **没有任何列举子文件的位**(只有 `resourceBase` 一个基址)
12
+ * ⇒ 宿主刻意留给 provider 的正是**这一半:枚举**。
13
+ *
14
+ * 六条硬性规则(逐条对应设计 §4.2,改这里先改那里):
15
+ * ① **只枚举,不注入正文** —— 索引里**绝不**出现任何参考文件的正文;
16
+ * ② **确定性排序** = 相对路径的 **UTF-8 字节序**(🔴 **不用 mtime**:mtime 随检出漂移,
17
+ * 会让每请求的注入文本抖动;对照 `src/cro.ts:101` 那份**落盘快照**用 {name,bytes,mtime} ——
18
+ * **同一件事两处的取舍相反,那个差异是判据不是巧合**);
19
+ * ③ **有界 + 响亮** —— 条数/字符各有上限,超出**必须显式写出还剩多少项**,**绝不静默截断**;
20
+ * ④ **摘要可复现** —— 取该文件首个 `#` 标题(剥 markdown 记号);取不到 ⇒ 留空,**不编**;
21
+ * ⑤ **本体文件特殊标注** —— `skill.ttl` 存在 ⇒ 该行标 🧬 + 顶部加一句"已建模";
22
+ * ⑥ **范围 = `references/` 一级条目 + `skill.ttl`** —— 子目录**给名字 + 计数、不展开、不读正文**
23
+ * (🆕 2026-10-11 由**真实输入**逼出的修订:原写"不进子目录",实测 `references/` 下确实有内容目录,
24
+ * 只报"有 N 个子目录"不给名字 = **不可行动**;折中仍在裁决 Q2「只披露名字、不注入正文」的意图内)。
25
+ *
26
+ * 🔴 本文件**只认 `<skillDir>/skill.ttl`**(owner 裁决 Q6:静默、只认固定名)。
27
+ * `references/*-domain.ttl`(旧名)一律当**普通参考件**:不特殊对待、不告警 —— 那 14 份由 CCC 自行改名。
28
+ * 🔴 「未建模」不是异常(裁决 Q6 / 判据 J9):无 `skill.ttl` 的**普通 skill** 照常产出索引,
29
+ * **零告警、零误差** —— 增强必须对普通 skill 完全向后兼容。
30
+ */
31
+ /** 第二层占位口:本 skill 的本体约束文件(固定名,设计 §5.1) */
32
+ export declare const SKILL_ONTOLOGY_FILE = "skill.ttl";
33
+ /** 参考件根(一级;不进子目录) */
34
+ export declare const REFERENCE_DIR = "references";
35
+ /** 条数上限(超出 ⇒ 写"还有 N 项未列出") */
36
+ export declare const RESOURCE_INDEX_MAX_ITEMS = 40;
37
+ /** 字符上限(超出 ⇒ 尾部响亮标记;宿主侧另有更外层的预算) */
38
+ export declare const RESOURCE_INDEX_MAX_CHARS: number;
39
+ export interface ResourceIndexOptions {
40
+ maxItems?: number;
41
+ maxChars?: number;
42
+ }
43
+ /**
44
+ * 取 markdown 首个 `#` 标题作摘要(规则④)。
45
+ *
46
+ * 细节(都由"可复现"推导):
47
+ * · **跳过 YAML frontmatter**(`---` 开头到下一 `---`)—— 否则题名行会被当成正文标题;
48
+ * · 只读文件**头部** `HEAD_BYTES`(一行标题不值得读整个大文件);
49
+ * · 剥掉行内记号(`*` `` ` `` `_` `#`)与首尾空白,压平内部连续空白;
50
+ * · 取不到 ⇒ `''`(**留空,不编** —— 编一个摘要等于让索引说假话);
51
+ * · 超长截断时**留省略号**(与规则③同一精神:截断要看得见)。
52
+ */
53
+ export declare function firstHeading(file: string): string;
54
+ /** 该 skill 是否已建模(只有固定名算 —— 裁决 Q6) */
55
+ export declare function hasSkillOntology(skillDir: string): boolean;
56
+ /**
57
+ * 生成第二层索引正文。
58
+ *
59
+ * @param skillDir skill 目录绝对路径
60
+ * @param options 上限覆盖(缺省用本模块常量)
61
+ * @returns 索引正文;**无可列项 ⇒ `''`**(规则⑥的一般化:`references/` 与 `skill.ttl` 都没有
62
+ * ⇒ 整块不产出 —— 空标题是噪声)。调用方按"空串不产出该块"处理。
63
+ */
64
+ export declare function buildResourceIndex(skillDir: string, options?: ResourceIndexOptions): string;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * skill-ontology.ts — **本体预留口**的发现与检查(纯逻辑,零 DSH 依赖)
3
+ *
4
+ * 设计真相源:`docs/serenity-skill-design.md` §4.1(四个动作)/§5.1-5.2(固定名与骨架)/§6(J1~J3)。
5
+ * 一句话:**一个 skill 若完成了本体建模,就在自己目录放固定名 `skill.ttl`**;
6
+ * 本模块回答三件事:**有没有**(发现)/**合不合法**(A 档)/**引用能不能兑现**(B 档)。
7
+ *
8
+ * 🔴 只认**固定名** `<skillDir>/skill.ttl`(owner 裁决 Q6:静默、CCC 自行修正):
9
+ * `references/*-domain.ttl` 这类**旧名一律当普通参考件** —— 不特殊对待、**不告警**、**不代读**。
10
+ * 🔴 「无 `skill.ttl`」**不是错误也不是告警**(裁决 Q6 / 判据 J9):
11
+ * 那是**普通 skill**,全程无声 —— 增强必须对普通 skill 完全向后兼容。
12
+ *
13
+ * ── 两档判据(**两档都锚在引擎自己的输出上**,我们不另写一遍 Turtle 解析)──────────
14
+ * **A1 结构合法**:`check --level strict` exit 0(引擎的 M1–M10)
15
+ * **A2 链上基础链**:`explain` exit 0 **且**报告里有 `Imports:` 行
16
+ *
17
+ * 🔴 **为什么必须有 A2(这是本模块存在的理由,且是实测逼出来的)**:
18
+ * **A1 绿 ≠ 链上了**。实测对照(同一天、同一引擎):
19
+ * · `home-rhetoric/references/home-rhetoric-domain.ttl`(写的是 `sr:imports`)
20
+ * ⇒ `check --level strict` = **✓ exit 0 · All checks passed**,而 `explain` **没有 `Imports:` 行**;
21
+ * · 注册表(写的是 `owl:imports`)⇒ `explain` **打印 `Imports: ccc-ontology.ttl`**(探针不瞎)。
22
+ * ⇒ **「检查全绿」与「链上了」是两件事**;只看 A1 就会把 14 份没链上的本体判成合格。
23
+ *
24
+ * 🟢 **A3(引用可兑现)**:**本模块刻意不做** —— 引擎已经做了,而且是**响亮的**。实测两条:
25
+ * · `check`:import 目标不存在 ⇒ **`ERR [M10] Ontology file not found: <abs>`**、**exit 1**;
26
+ * · `explain`:同一个文件 ⇒ **exit 2 · `ONTO_FILE_NOT_FOUND`**。
27
+ * ⇒ 再写一遍就是**同一件事的第二实现**(本仓的老账:"先问谁已经做了")。
28
+ * 🔴 但**起因值得记**:我原本以为引擎对缺失 import 是**静默**的(照源码 1687 行那句 "Fall back to
29
+ * loading just this file (no imports)" 推断)—— **实测把这条推断打假了**(两条路都响亮报错)。
30
+ * ⇒ 又一次「**照源码的意图抄 ≠ 引擎实际怎么行为**」(D119 同族)。
31
+ */
32
+ /** 三态(**互可分辨**是硬要求,判据 J3) */
33
+ export type SkillOntologyState = 'none' | 'ok' | 'error';
34
+ export interface SkillOntologyCheck {
35
+ id: 'A1' | 'A2';
36
+ ok: boolean;
37
+ detail: string;
38
+ }
39
+ export interface SkillOntologyInspection {
40
+ state: SkillOntologyState;
41
+ /** 容器内相对路径(未建模 ⇒ '');调用方负责把 root 去掉 */
42
+ rel: string;
43
+ modeled: boolean;
44
+ checks: SkillOntologyCheck[];
45
+ /** A2/A3 用:引擎报告的 import 目标(原样,未解析) */
46
+ imports: string[];
47
+ }
48
+ export interface InspectOptions {
49
+ /** CCC 根(引擎的解析基准) */
50
+ root: string;
51
+ }
52
+ /**
53
+ * 检查一个 skill 的预留口。
54
+ *
55
+ * @param skillDir skill 目录(绝对路径)
56
+ * @param opts.root CCC 根(引擎基准;**必须是 CCC 根**,否则引擎找不到容器级资产)
57
+ * @returns 三态 + 逐档判据
58
+ */
59
+ export declare function inspectSkillOntology(skillDir: string, opts: InspectOptions): SkillOntologyInspection;
60
+ /**
61
+ * 渲染三态报告(**三态必须互可分辨** —— 判据 J3;措辞刻意不许糊)。
62
+ *
63
+ * 为什么单独一个函数:`check` 的呈现(P3 的工具)与测试看的是**同一段文本**,
64
+ * 两处各写一遍必然漂移(本仓"路径写两遍必漂"的既有教训)。
65
+ */
66
+ export declare function formatSkillOntologyInspection(skillName: string, insp: SkillOntologyInspection): string;
@@ -18,6 +18,8 @@ type EntrySkillSource = 'serenity-marker' | 'pointer' | 'opencode' | 'dsh';
18
18
  interface EntrySkill {
19
19
  name: string;
20
20
  content: string;
21
+ /** skill 目录(= SKILL.md 的父目录)—— 第二层披露树要按目录扫 `references/`(serenity-skill P1) */
22
+ dir: string;
21
23
  source: EntrySkillSource;
22
24
  }
23
25
  /**
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 《serenity-skill 编写与优化方法》(`guide` 印它)。
3
+ *
4
+ * 边界(D23):**本文只是方法**;判据在代码里(P1/P2),语义审查在引擎的 `compliance` 里 ——
5
+ * 三者**不许互相抄**(同一件事写两遍必然漂移)。
6
+ */
7
+ export declare const SKILL_CREATOR_GUIDE = "# serenity-skill \u2014\u2014 \u589E\u5F3A\u578B skill \u7684\u7F16\u5199\u4E0E\u4F18\u5316\u65B9\u6CD5\n\n## \u4F55\u65F6\u8BE5\u5EFA\u6A21\uFF0C\u4F55\u65F6\u4E0D\u8BE5\n\n**\u8BE5\u5EFA\u6A21**\uFF1A\u8BE5\u9886\u57DF\u6709**\u7A33\u5B9A\u7684\u7C7B\u578B\u4E0E\u5173\u7CFB**\uFF08\u8C01\u662F\u8C01\u3001\u4E00\u5BF9\u591A\u3001\u4EC0\u4E48\u5FC5\u987B\u5148\u4E8E\u4EC0\u4E48\uFF09\uFF0C\u4E14\u8FD9\u4E9B\u7ED3\u6784**\u4F1A\u53CD\u590D\u88AB\u5F15\u7528**\n\uFF08\u6392\u9519\u3001\u5BA1\u67E5\u3001\u5199\u6587\u6863\u90FD\u8981\u56DE\u53BB\u770B\u5B83\uFF09\u2014\u2014 \u5178\u578B\u5982\"\u4E00\u4E2A skill \u7BA1\u54EA\u4E9B\u8D44\u4EA7\u3001\u8D44\u4EA7\u4E4B\u95F4\u4EC0\u4E48\u7EA6\u675F\"\u3002\n\n**\u4E0D\u8BE5\u5EFA\u6A21**\uFF08**\u4E0D\u5EFA\u6A21\u4E0D\u662F\u7F3A\u9677**\uFF09\uFF1A\n\u00B7 \u7EAF\u64CD\u4F5C\u89C4\u7A0B\uFF08\"\u5148\u8DD1 A \u518D\u8DD1 B\"\uFF09\u2014\u2014 \u90A3\u662F SKILL.md \u7684\u6B63\u6587\uFF0C\u7ED9\u5B83\u5957\u672C\u4F53\u53EA\u662F\u6362\u4E2A\u5730\u65B9\u5199\u6563\u6587\uFF1B\n\u00B7 \u4E00\u6B21\u6027/\u77ED\u547D\u7684\u4E8B\uFF1B\n\u00B7 \u4F60\u81EA\u5DF1\u90FD\u8FD8\u8BF4\u4E0D\u6E05\u9886\u57DF\u91CC\u5230\u5E95\u6709\u54EA\u51E0\u7C7B\u4E1C\u897F\u65F6 \u2014\u2014 **\u5148\u5199\u6563\u6587\uFF0C\u7B49\u7ED3\u6784\u6D6E\u73B0\u518D\u5EFA\u6A21**\uFF08\u5EFA\u6A21\u4E0D\u662F\u5165\u573A\u5238\uFF09\u3002\n\n## \u4E09\u4E2A\u52A8\u4F5C\uFF08\u505A\u5B8C\u5373\"\u5DF2\u5EFA\u6A21\"\uFF09\n\n1. \u5728 skill \u76EE\u5F55\u653E**\u56FA\u5B9A\u540D** `skill.ttl`\uFF08\u4E0E `SKILL.md` \u5E76\u5217\uFF09\u2014\u2014 \u8FD9\u662F**\u552F\u4E00**\u88AB\u8BA4\u7684\u540D\u5B57\u3002\n \u26A0\uFE0F \u65E7\u5199\u6CD5 `references/<name>-domain.ttl` **\u4E0D\u88AB\u8BA4**\uFF08\u9759\u9ED8\u5F53\u666E\u901A\u53C2\u8003\u4EF6\uFF09\uFF1A\u4E0D\u662F\u62A5\u9519\uFF0C\u662F**\u6CA1\u6709\u4EBA\u4F1A\u770B\u5B83**\u3002\n2. `skill.ttl` **\u5FC5\u987B\u94FE\u4E0A\u57FA\u7840\u94FE**\uFF0C\u5F62\u5F0F\u662F `owl:imports <\u76F8\u5BF9\u8DEF\u5F84>`\uFF1A\n ```turtle\n :onto a owl:Ontology ;\n sr:name \"<skill-name>\" ;\n owl:versionInfo \"0.1.0\" ;\n owl:imports <../home-serenity/references/serenity-skills-ontology.ttl> .\n ```\n \uD83D\uDD34 **\u76F8\u5BF9\u8DEF\u5F84\u7531 `create` \u7B97**\uFF08\u4ECE\u672C skill \u76EE\u5F55\u6307\u5411\u9876\u5C42 skill \u7684 references/\uFF09\uFF0C**\u4E0D\u8981\u4EBA\u8089\u63A8**\u3002\n \uD83D\uDD34 \u5F15\u64CE**\u53EA\u89E3\u6790 `owl:imports`**\uFF1B\u5199 `sr:imports` **\u4E0D\u4F1A\u88AB\u8BFB**\uFF0C\u800C\u4E14 `check` \u5BF9\u5B83**\u7167\u6837\u62A5\u5168\u7EFF** \u2014\u2014\n \u6240\u4EE5\u522B\u53EA\u770B `check` \u7EFF\u5C31\u4EE5\u4E3A\u94FE\u4E0A\u4E86\uFF08\u8FD9\u6B63\u662F\u672C\u5DE5\u5177 `check` \u8981\u540C\u65F6\u8DD1 A2 \u7684\u539F\u56E0\uFF09\u3002\n3. \u8BA9 `check` \u5168\u7EFF\uFF08A1 \u7ED3\u6784\u5408\u6CD5 \uFF0B A2 \u94FE\u4E0A\u57FA\u7840\u94FE\uFF09\u3002\n\n## \u4E24\u5C42\u62AB\u9732\u6811\u7684\u5199\u6CD5\u4E60\u60EF\uFF08\u5F71\u54CD\u6A21\u578B\u80FD\u4E0D\u80FD\u627E\u5F97\u5230\uFF09\n\n\u00B7 `references/` \u91CC\u7684\u6587\u4EF6\u540D**\u5C31\u662F\u76EE\u5F55**\uFF1A\u524D\u7F00\u6570\u5B57\uFF08`00-`\u2026`90-`\uFF09\u7ED9\u7A33\u5B9A\u6B21\u5E8F\uFF0C\u540D\u5B57**\u5199\u6210\u80FD\u8BFB\u61C2\u7684\u4E00\u53E5**\u3002\n\u00B7 \u6BCF\u4E2A\u53C2\u8003\u6587\u4EF6\u7684**\u9996\u4E2A `#` \u6807\u9898**\u4F1A\u88AB\u5F53\u4F5C\u7D22\u5F15\u91CC\u7684\u6458\u8981 \u21D2 **\u7ED9\u6BCF\u4E2A .md \u5199\u4E00\u4E2A\u8BF4\u4EBA\u8BDD\u7684\u9996\u6807\u9898**\uFF08\u522B\u5199\u6587\u4EF6\u540D\u590D\u8BFB\uFF09\u3002\n\u00B7 \u7D22\u5F15**\u53EA\u5217\u4E0D\u8BFB**\uFF1A\u6B63\u6587\u6C38\u8FDC\u7559\u7ED9\u6309\u9700 `read` \u21D2 \u522B\u628A\u5927\u6BB5\u5185\u5BB9\u585E\u8FDB SKILL.md\uFF08\u5B83\u6BCF\u8BF7\u6C42\u90FD\u6CE8\u5165\uFF09\u3002\n\u00B7 `references/` \u4E0B\u7684**\u5B50\u76EE\u5F55**\u4F1A\u88AB\u5217\u6210\u4E00\u884C\uFF08\u540D\u5B57\uFF0B\u6587\u4EF6\u6570\uFF09\uFF0C**\u4E0D\u5C55\u5F00** \u21D2 \u76EE\u5F55\u540D\u8981\u8D77\u5F97\u8BA9\u4EBA\u77E5\u9053\u91CC\u9762\u662F\u4EC0\u4E48\u3002\n\n## \u4ECE\u9886\u57DF\u6563\u6587\u5230 ttl\uFF08\u5177\u4F53\u6B65\u9AA4\uFF09\n\n1. \u628A SKILL.md \u91CC\u7684\u540D\u8BCD\u5708\u51FA\u6765 \u2192 \u6311\u51FA**\u53CD\u590D\u51FA\u73B0\u3001\u6709\u8EAB\u4EFD**\u7684\u90A3\u4E9B = \u5019\u9009\u7C7B\u578B\uFF1B\n2. \u7ED9\u6BCF\u4E2A\u7C7B\u578B\u5199\uFF1A`rdfs:label` \uFF0B `sr:rigidity`\uFF08`rigid` \u8EAB\u4EFD\u56FA\u5B9A\uFF0F`anti-rigid` \u4F1A\u53D8\uFF09\uFF0B \u8EAB\u4EFD\u5C5E\u6027\uFF08`sr:identity`\uFF09\uFF1B\n3. \u628A\u52A8\u8BCD\u5708\u51FA\u6765 \u2192 \u6709\u65B9\u5411\u3001\u6709\u57FA\u6570\u7684 = \u5173\u7CFB\uFF08`rdfs:domain`/`rdfs:range` \uFF0B `sr:cardinality`\uFF09\uFF1B\n4. \u628A\"\u6C38\u8FDC\u6210\u7ACB\u7684\u8BDD\"\u5199\u6210\u516C\u7406\uFF08`sr:axiom`\uFF09\u2014\u2014 **\u53EA\u5199\u771F\u80FD\u5224\u771F\u5047\u7684**\uFF0C\u522B\u5199\u53E3\u53F7\uFF1B\n5. \u8865 `sr:source`/\u6765\u6E90\uFF08\u5BF9\u5916\u90E8\u4E3B\u5F20\uFF09\u3002\n**\u65B9\u6CD5\u6B63\u6587\u4E0D\u5728\u8FD9\u91CC\u91CD\u590D**\uFF1A\u89C1 `praxis ontology`\uFF08\u672C\u4F53\u5DE5\u7A0B\u65B9\u6CD5\u7FA4\uFF1A\u56DB\u7EC4\u5224\u636E M1\u2013M10\uFF0FL1\u2013L5\uFF0FD1\u2013D8\uFF0FV1\u2013V4\u3001OEPE \u56DE\u8DEF\u3001\u53CD\u6A21\u5F0F\uFF09\u3002\n\n## \u4F18\u5316\u56DE\u8DEF\uFF08\u6539\u5B8C\u4E4B\u540E\u518D\u8D70\u4E00\u904D\uFF09\n\n```\nserenity-skill-creator index <skill> \u2190 \u5148\u770B\u6A21\u578B\u5230\u5E95\u4F1A\u770B\u5230\u4EC0\u4E48\uFF08\u9732\u6CA1\u9732\u3001\u987A\u5E8F\u5BF9\u4E0D\u5BF9\uFF09\nserenity-skill-creator check <skill> \u2190 A1 \u7ED3\u6784 \uFF0B A2 \u94FE\u4E0A\uFF08\u673A\u68B0\uFF0C\u5FC5\u987B\u5168\u7EFF\uFF09\nserenity-skill-creator review <skill> \u2190 C \u6863\uFF1A\u6587\u5B57\u4E0E ttl \u9010\u6761\u7684\u5408\u89C4\u95EE\u9898\u6E05\u5355\uFF08\u8981\u4EBA\u5728\u73AF\u7B54\uFF09\n\uFF08\u6539 SKILL.md / skill.ttl / references\uFF09\n \u2191__________________________ \u56DE\u5230\u7B2C\u4E00\u6B65\n```\n\n**\u5224\u636E**\uFF1A\u6539\u5B8C**\u5FC5\u987B\u91CD\u8DD1** `check` \u2014\u2014 \u672C\u4ED3\u7684\u6559\u8BAD\u662F\"\u68C0\u67E5\u6CA1\u8DD1\"\u548C\"\u68C0\u67E5\u901A\u8FC7\"\u5728\u8F93\u51FA\u4E0A\u5FC5\u987B\u5206\u5F97\u5F00\uFF1B\n`review` \u51FA\u7684\u662F**\u95EE\u9898\u6E05\u5355**\uFF08\u534A\u673A\u68B0\u3001\u8981\u4EBA\u56DE\u7B54\uFF09\uFF0C**\u4E0D\u662F**\u901A\u8FC7/\u4E0D\u901A\u8FC7\u3002\n";
8
+ export declare function createSkillCreatorTool(): import("@deepseek-ai/dsh-tools").ToolDefinition;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shgroup/dsh-serenity-hooks",
3
- "version": "1.52.3",
3
+ "version": "1.54.0",
4
4
  "description": "宁静号 ACC harness(Native Cordis 插件)——给 DeepSeek Harness 装一个「AI 工作区」:13 个工具(container_fs/container_trajectory/dashboard/container_git/msm/praxis/ontology/handyman/localstore/container_admin;另三个 im-bridge/acc-diag/diagram 按配置条件出现)+ 机械约束(安全模式/工作区围墙/密钥守卫/对外输出守卫)+ 工作日志与原地重建 + 网页登录入口/微信桥/子角色/对外问答页/trajectory 唤醒注册表。适配 DSH 0.1.7-rc.1。",
5
5
  "license": "MIT",
6
6
  "repository": {