shiyan688/dsh-novel-craft ↗★ 0

dsh-novel-craft

提供小说创作工作台及作者偏好档案提炼功能 适合小说创作者,通过👍/👎反馈让AI学习并模仿个人写作风格

套件
dsh-novel-craft
相容性
待驗證
Harness 依賴範圍
*
Cordis 依賴範圍
*
版本
0.2.0
授權
MIT
最近更新
2026年9月17日

同名套件的其他儲存庫

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:shiyan688/dsh-novel-craft

dsh-novel-craft · 小说创作工作台

npm License: MIT dsh-plugin

English: a novel-writing workbench for DeepSeek Harness (dsh) that learns your taste instead of writing generic prose. See README.en.md.

核心那一件事:把一轮里「结构不同」的候选稿摊开,作者只点 👍好 / 👎坏;标注被压成 可复用的写作规律写进 作者偏好档案.md,驱动下一轮收敛。原文另存,不进写稿上下文。 整个 dsh 插件生态里,做"让 AI 写出合格稿"的很多,做"让 AI 写出像你的稿"的只有这一条路。

口径说明(不说大话):这句"原文不进写稿上下文"是准确的,它指的是—— 写稿/写候选/要方向时,模型只拿到写作包,而包里除了上一章结尾(≤900 字,接续用,包内注明) 之外没有任何正文原文;标注原文、证据摘录、批注都另存为文件,写稿一律不读。 但有三处作者亲手点下的按钮会触发一次辅助调用并带上原文,各自有硬上限: ①「提炼规律」把标过的段落(≤40 段 × 240 字、总计 ≤20KB)交给模型压成规律; ②「补齐来源」把证据摘录里的引文(每条裁 160 字)与规律一起交给模型判断对应关系; ③「按批注微调」把被批注的段落(每段裁 800 字)连同前后段各 80 字交给模型改写。 除这三处,没有任何路径把正文原文送进模型。

围绕这一件事,0.2 起它是一个能陪你把一本书写完的工作台:

能力一句话
✍️ 开新章写新的一章:让模型给 8-12 个场景决策方向(不是换词)→ 一次一篇写候选 → 去抽卡选段 → 合并成定稿并写入正文
🎴 抽卡 + 偏好档案你标好坏 → 蒸馏成规律 → 规律是唯一进上下文的"口味输入"(含规律→证据反查:点开一条规律看它从哪几段来)
🗂 章节看板自动认出作品根(正文/设定/人物/剧情在哪一层),一章一行:字数、轮次、标注、批注、写作包、读者评分、张力、体检问题
📦 每章写作包把这一章要用的东西组装成一个文件(本章任务 + 规律 + 上一章结尾 + 前情 + 人物 + 台账 + 情节节点 + 伏笔欠账 + 禁 AI 腔清单 + 写作要求),并给出上下文预算表;写稿会话只读它
💬 批注式微调正文里读到哪儿不对,点那段按 A 留批注;「按批注微调」只改被批注的段落,其余逐字不动(两道防线核对),采纳前逐段对照,写回时自动备份原稿
📈 情节体检张力曲线(手工标 / 自动估)、大纲偏移、伏笔欠账、字数失衡、事件密度、评分下滑;全部本地统计,正文不进模型上下文
🧭 全书阶段立项 → 设定 → 人物 → 大纲 → 逐章正文 → 修订 → 完本:产物清单 + 门禁 + 一键起草(模型写初稿,你改)

工作台是 dsh 的真插件:侧栏入口 + 全屏浮层,浏览器半区(界面)与宿主半区 (读写文件、回环 HTTP API)分离。它读你已有的目录习惯:逐弈登仙-第8章.txt、 剧情/第 8 章 剧情总结.md、人物/*.json、设定/道具与增益台账.md、 factory/runs//第N章/候选稿/、评论/第N章_评论数据.json 都认,不需要为了用它重新整理文件。

安装

dsh plugin --profile web add dsh-novel-craft                     # 从 npm
dsh plugin --profile web add github:shiyan688/dsh-novel-craft    # 或直接从 GitHub

已发布:dsh-novel-craft@0.1.1(npm 页面)。

装完重启一次该 profile(bundle 与 client 元数据在进程内缓存)。重启后侧栏底部 出现「🎴 抽卡工作台」。

要求:dsh 的 web profile(dsh-web-app),并且挂载了 LLM 服务(ctx.llm)—— 「提炼规律」那一步要调一次辅助模型。没挂也能用:标注照点,档案照编辑,只是不能自动提炼。

配套 skill(可选,但强烈建议装)

插件负责"让你低负担地点",skill 负责"告诉 agent 怎么用这些标注"。skills/ 下有两个:

cp -r skills/taste-calibration skills/novel-writing /.dsh/skills/   # 或 ~/.dsh/skills/
  • taste-calibration — 抽卡式作者品味校准(本插件自研核心):怎么造"结构不同"的候选、怎么把选段转成证据、怎么在下一轮收敛;
  • novel-writing — 中文网文写作手艺:禁AI腔(六维度判定)、多视角信息差、术语与账目口径、克制留白。

界面预览

静态快照(由真实组件渲染生成,不是手画的图):docs/preview.html —— clone 下来用浏览器打开即可,页签切换「抽卡 / 偏好档案 / 目录选择器」三个界面。 改完组件跑 node scripts/build-preview.mjs 就能刷新。

🎴 抽卡工作台   [📁 第9章/候选稿 ▾]  [↻]  [✕]
[🎴 抽卡]  [📋 偏好档案 · 2]
📂 /你的作品/factory/runs/逐弈登仙/第9章/候选稿
[A · 保守精修 2.1k字 · 标 3]  [B · 苏清鸢识药 1.8k字]  [C · 玩家算计 1.9k字] …
──────────────────────────────────────────────────────────
  天蒙蒙亮,一行人踏着露水朝望舒城城门走去。        (光标段浮出 👍 👎)
│ 为首的是金海,浅青色宗门弟子服纤尘不染。……        ← 左边绿条=标了好
│ 老道捋了捋山羊胡,叹了口气:“你性子急躁……”       ← 左边红条=标了坏
  宁陈站在队尾,指节在袖子里轻轻蹭了一下。
第 4 / 28 段 · j / k 换段 · G 标好 · B 标坏 · 空格 取消 · n / p 换篇
[⚗️ 提炼规律(12)]                                   [📋 偏好档案]

30 秒上手

只想让下一次抽卡更像你(抽卡这条线):

  1. 侧栏点「🎴 抽卡工作台」→ 顶栏「📁」挑候选稿目录(推荐/最近/浏览/手动四种入口);
  2. 读:连续正文,j/k 换段、G 标好、B 标坏、空格 取消、n/p 换篇;
  3. 点「📋 偏好档案」→「⚗️ 提炼规律」→ 勾选采纳 → 档案里只留下规律。
  4. 想追一条规律"凭什么":点规律后面的「🔍 证据」——模型标了编号的就直接列出那几段; 老档案(提炼时还没记编号)点「补齐来源」走一次很便宜的模型调用补上。

想在工作台里把一本书写完(作品这条线):

  1. 要写新的一章:点「🗂 章节」→「✍️ 开新章」→ 要方向 → 写候选 → 去抽卡选段 → 合并写入正文(详见下面那一节);
  2. 顶部「📁」选任意一个候选稿目录,工作台会自己向上认出作品根(也可以在「🗂 章节」页看到它认出的路径);
  3. 「🗂 章节」页就是这本书的看板:点章节标题进正文,右侧四个页签 = 批注 / 写作包 / 微调 / 本章设定;
  4. 写新章之前:先在「本章设定」里写"这一章要干什么"→ 点「生成写作包」→ 把那个 .md 交给写稿会话(只给它这一个文件);
  5. 写完读一遍,不对的地方点那段按 A 留批注 →「按批注微调」→ 逐段对照没问题再「采纳并写回」;
  6. 觉得整体节奏不对:去「📈 情节」看张力曲线与问题清单;要补立项/大纲这类产物:去「🧭 全书」。

用了什么

位置做什么
侧栏底部「🎴 抽卡工作台」开/关面板
顶栏「📁 …」目录选择器:推荐 / 最近使用 / 浏览 / 手动输入
「🎴 抽卡」页连续正文阅读视图:段落之间不打断,标记按钮只在光标/悬停的那段浮出;键盘流 j/k 换段、G 标好、B 标坏、空格 取消、n/p 换篇
「📋 偏好档案」页只要规律:规律清单 + 提炼审阅 + 标注统计 + 来源回跳 + 就地编辑;每条规律后面有「🔍 证据」可反查它从哪几段来;原文证据收在折叠区
「🗂 章节」页章节看板:一章一行(字数/状态/轮次/标注/批注/写作包/评分/张力/体检);点标题进单章视图
单章视图右侧四个页签:💬 批注(就地留改稿清单)、📦 写作包(上下文预算 + 全文)、✍️ 微调(前后对照 + 采纳写回)、📝 本章设定(这一章要干什么)
「📈 情节」页张力曲线(实线=你手工标的,虚线=按正文估的)+ 问题清单 + 未兑现伏笔;「重跑体检」按需算
「🧭 全书」页七个阶段(立项 → 完本):产物勾选、门禁提示、一键起草本阶段产物
底部「⚗️ 提炼规律」标注原文 → 规律条目,勾选后写进 /.dsh-novel-craft/作者偏好档案.md

标注落在 /.dsh-novel-craft/marks.json,随作品走,不写全局配置。 候选稿目录存在 dsh settings 的 dsh-novel-craft 命名空间里(candidateDir)。

用抽卡写新的一章(完整流程)

这是本插件的主线用法。抽卡解决"哪一段好",开新章解决"候选稿从哪来、选完怎么合起来"。

在「🗂 章节」页点右上角「✍️ 开新章」,四步:

  1. 这一章要写什么:章号(默认给"下一章")、本章设定(会进写作包第 ① 节;如果 大纲.md 里有这一章的节点,会带出来给你参考)、要几个方向(默认 10)、本轮额外要求。

  2. 方向:让模型给一批「场景决策」方向,每个方向一句话说清"这一篇试什么"。示例: A 保守精修:以最小改动保留现有骨架 / B 配角识货:让同行配角先认出货色,主角的算计藏在他的沉默里 / C 双层信息差:让掌柜也在算计,读者比主角先看出一层。 方向就是这一批的卡池:可以改名、改说明、去掉不想要的,勾选哪些就写哪些。 口味是怎么进去的:写方向与写候选用的输入都只有写作包,而写作包的第 ② 节就是 作者偏好档案(只有规律、已档掉工作台的账目自动块)。写候选时还会把它念两遍: 系统提示点名"第 ② 节里『避免的写法』是作者明确否决过的,一条都不要出现", 用户提示的末尾再把这份否决清单重述一次——写作包有三四千字,规律放中间容易被后文冲淡, 末尾重申一遍只花两三百字。

  3. 写候选:一次一篇地写,写完一篇立刻落盘成 factory/runs//第N章/候选稿/第N章-A-保守精修.txt(沿用你自己的命名习惯)。 看得见进度(第 3/10 篇),可以中途停,已经写好的不会丢;单篇失败只影响那一篇,重来一次就行。 点「🎴 去抽卡选段」——工作台会顺手把当前卡池设成刚写出来的这个目录,你不用记路径。

  4. 合并定稿:回「🗂 章节」→「✍️ 开新章」→ 第 ④ 步。这里列出你在抽卡页标了 👍 的段落(按稿件顺序), 并带上你写的取用原因,成为一个「取用段落表」。跨稿衔接处会写成 〔此处需过渡〕 标记 —— 工作台不会让模型替你补写,那些位置留给你用批注式微调一处一处处理。 点「写入正文」,正文写成 第N章 标题 + 段落的规范格式,原稿自动备份到 .dsh-novel-craft/备份/。

在抽卡页给某个好段写一句「取用原因」(标 👍 之后会出现 ✎ 按钮),合并时它会自动进表格—— 这份记录就是 筛选与合并记录.md,和你在第 8 章手工写的那份格式一致。

新增产物都放在哪儿

文件内容进不进上下文
/写作包.md(没有轮次目录时退到 .dsh-novel-craft/写作包/第N章 写作包.md)写稿会话唯一该读的文件✅ 就该它进
/.dsh-novel-craft/批注/第N章.json你的改稿批注(机器可读)❌ 只在点「微调」时取被批注的那几段
/.dsh-novel-craft/章节设定/第N章.md本章目标 / 禁止发生 / 出场人物 / 要求✅ 作为写作包的第 ① 节
/.dsh-novel-craft/规则来源.json规律 → 证据的对应表❌ 只给界面反查
/.dsh-novel-craft/微调/第N章 微调稿.md + 原稿备份/微调前后对照、写回前的原稿❌ 给人看的
/.dsh-novel-craft/workspace.json阶段进度、手工张力、章节状态覆盖❌
/.dsh-novel-craft/取用理由.json好段的"为什么用这一段"(合并进记录表)❌
/第N章/候选稿/、定稿候选/、筛选与合并记录.md开新章写出来的候选、合并稿、取用记录❌(只有写候选时喂写作包)

写作包里有哪十节:① 本章任务(你写的)② 作者偏好档案(只有规律)③ 上一章结尾(只取结尾,接续用) ④ 前情提要(来自各章剧情总结,不重读正文)⑤ 本章人物 ⑥ 道具与增益台账(本章相关 + 当前持有) ⑦ 情节节点(上一章/本章/下一章)⑧ 未兑现伏笔 ⑨ 禁 AI 腔清单 ⑩ 写作要求。 每节都有上限,整包默认 9000 字的预算;超了就按"先削补得回来的、后削没它写不了的"顺序削, 削了谁、削了多少都写在返回的预算表里。包尾固定有一段「不要读进上下文的东西」,把证据摘录、marks.json、批注、全书合并稿点名列为禁区。

批注式微调:模型只能改你标过的那几段

作者读到哪儿不对,点那段按 A(或双击)留一条批注——标签(AI 腔 / 啰嗦 / 情绪直给 / 平 / 人物失真 / 逻辑账目 / 信息差 / 其他)+ 一句自由文字。 点「按批注微调」时才把被批注的那几段(每段裁到 800 字)连同批注、以及前后段各 80 字(供模型贴着邻段语气改)交给模型; 其余段落它碰不到。分两道防线:

  1. 拼装防线:applyRevision 用「原文段落 + 改写表」重新拼,模型没有机会碰到没批注的段落;
  2. 核对防线:verifyRevision 再逐段比对一次——没批注的段落只要有一个字不同就拒绝写回;段落数变了也拒绝。

核对结果分两档,界线是"你有没有授权":

  • 拦住的(error):没批注的段落被改了、段落数变了 → 一律不许写回,界面把问题列出来;
  • 只提示的(warn):批注过的段落字数变化过大(你的批注本来就可能写着"这段砍一半")→ 摆出前后对照,你自己决定。

采纳写回时先把原稿备份到 .dsh-novel-craft/微调/原稿备份/第N章 .md,批注自动标成已处理; 模型的原始输出也留档(微调/第N章 原始输出.md)——格式没对上时,靠它排查而不是靠猜。

情节体检:不把正文喂给模型

「📈 情节」页的结论全部来自本地字符串统计,一页正文都不会进模型上下文:

检查判据说话方式
中段塌陷 / 全程平缓连续 ≥3 章张力 ≤2(且后面有更高的章)给章号区间 + 可执行建议
大纲偏移 / 大纲缺失大纲节点关键词与该章剧情总结的覆盖率低于阈值压到 0.2 阈值,宁少报;拿不准降级为 info 并写"需人工确认"
伏笔超期大纲里「埋」了但到最后一章还没「收」,且已欠 ≥5 章给出埋的章号与欠的章数
字数失衡 / 事件密度异常与该书平均字数、章节剧情点条数对比info 级,列出章号
评分下滑连续 ≥2 章下降(累计 ≥0.15),或低于均值 ≥0.8样本 ` 自动产出:
在临时目录里真装一份该版本的 dsh、按 profile 的方式挂上插件、用独立 DSH_HOME 起服务,
断言宿主路由可用 / 客户端半区进启动清单 / 半区能下发且内容正确。

注:0.1.0-rc.x、0.1.1-rc.x 这几个更老的版本现在已无法从 npm 全新安装 (包元数据都能取到,但解析/拉包阶段会挂死,属上游制品问题),所以自动化矩阵的老基线 取 0.1.2-rc.1;作者本机长期跑在 0.1.0-rc.7(CLI)/0.1.0-rc.8(组件)上, 这一档由日常使用覆盖。

已知的版本差异(不影响本插件,但值得知道):

  • 新版 web 服务默认带 token 鉴权:直接打开 http://127.0.0.1:/ 会 401, 要用启动日志里那条带 ?token= 的地址;插件自己的回环路由不在此列。
  • 客户端 bundle 的下发地址变了:新版是组合脚本 /plugins/??a/client.js,b/client.js&rev=…,旧版是 /plugins//client.js。 插件无需关心(名册会给出地址),但自己写裸 URL 测试的人会踩到。

怎么持续跟进(自动化)

node scripts/check-dsh-compat.mjs          # 默认测 npm 的 next
node scripts/check-dsh-compat.mjs alpha    # 或 latest / alpha / 具体版本号

脚本会在临时目录里真装一份指定版本的 dsh、按 profile 的方式挂上本插件、用独立 DSH_HOME 起服务,然后断言三件事:宿主路由可用、客户端半区进入启动清单、半区能下发且内容正确。 跑完自动清理,不影响你本机正在用的实例(CI 里由 .github/workflows/compat.yml 每周一跑 latest / next / alpha / 0.1.0-rc.7 四档)。

版本策略:一条代码线,不拆分支

我们不为不同 dsh 版本维护不同的插件版本。理由:实测下来需要的 API 面在 0.1.0-rc.7 与 0.1.6-alpha.1 之间没变,拆分支只会让用户不知道该装哪个。做法是:

  • 服务一律按需获取 + 特性探测(ctx.get() 拿可选项、ctx.inject() 等就绪、缺了就降级);
  • 出问题优先补兼容代码而不是发兼容版本;
  • 只有当某个版本真的移除了关键能力、无法共存时,才会另发一条 legacy 线并在这里写明 (npm dist-tag 可以同时挂 latest 与 legacy,用户装哪个都明确)。

踩过的两个版本坑(写给插件作者)

  1. 行的 apply 可能早于服务挂载:在较新的 dsh 里,同步 apply 中 ctx.get('webServer') 会拿到 undefined(实测 0.1.6-alpha.1)。本插件因此改成 ctx.inject(['settings', 'webServer'], …) 等两个服务都就绪,并带超时兜底—— 写成"直接 get、拿不到就 return"的插件会在新版本里静默什么都不做。
  2. 客户端 bundle 的下发地址变了:新版是组合脚本 /plugins/??a/client.js,b/client.js&rev=…,旧版是 /plugins//client.js。 插件本身不用关心(名册给出地址),但拿裸 URL 做测试的人会以为"插件没被收录"。

宿主半区对 @deepseek-ai/dsh-llm 采用惰性加载:万一将来某个版本改了它的包名或导出, 插件其余功能(标注、证据摘录、档案)照常可用,只有「提炼规律」这一步会给出明确提示。

三层产物:标注 / 证据摘录 / 规律档案

这是本插件的核心约定——进写稿上下文的只有规律(三处按钮级例外见上方"口径说明"):

文件(都在 /.dsh-novel-craft/)给谁看会不会进上下文
marks.json工作台(机器可读的好/坏标注)❌ 写稿永不读;只有点「提炼」时以编号形式进一次辅助调用
证据摘录.md人回查 + 提炼的输入(原文)❌ 写稿永不读;只有点「提炼」「补来源」时进那一次辅助调用
作者偏好档案.md写稿会话只读这一份(只有规律)✅ 就它
# 作者偏好档案
> 这里只放能复用的写作规律,不放原文摘录——写稿时读这一份就够。
> 原文证据在 `证据摘录.md`(给人回查、给提炼当输入),不要读进写稿上下文。

## 已验证偏好(作者喜欢什么)
- 危险场面只写结果落到谁身上,不写招式过程
- 用一句干巴巴的话接住重击,不铺排情绪

## 避免的写法(作者不喜欢什么)
- 别堆叠"宛如/像是"式的比喻
- 别让角色用解释性台词交代设定

   ← 工作台维护的账目,每次提炼重写
- 标注:30 段(👍 15 / 👎 15),覆盖 3 篇
- 待提炼:0 段
- 最近提炼:2026-09-16 17:02 · deepseek-official/deepseek-v4-flash

规律是怎么来的:一次便宜的辅助调用

  1. 作者在「抽卡」页点好/坏 → 落 marks.json;
  2. 「📄 更新证据摘录」把原文收进 证据摘录.md(也可由提炼自动触发);
  3. 「⚗️ 提炼规律」把待提炼的原文(默认最多 40 段、每段 240 字、总 20KB) 交给一次辅助模型调用,系统提示强制它只输出 + 规律 / - 禁忌,禁止抄原文;
  4. 结果不直接进档案,而是列出来让作者逐条勾选,「采纳选中」才写进规律区, 并把提炼水位推到这批标注(下次不会再送一遍)。
  • 用哪条模型路由:默认取 dsh 当前的默认模型;想指定便宜档位就在设置里填 dsh-novel-craft 的 distillProvider / distillModel。
  • 不想让任何模型碰原文?跳过第 3 步,把 证据摘录.md 交给你的 agent 也可以—— 反正进档案的只有规律。
  • 没有规律区内容的档案会显示空态引导;作者手写的任何内容都会被完整渲染、不会被隐藏或覆盖。
  • 自动块(begin / end 之间)每次提炼重写;规律区是作者的,永远不动。

宿主半区 HTTP API(回环限定)

全部在 /novel-craft/api/ 下,只服务 127.0.0.1,enabled: false 时整体 503:

方法路径作用
GETstate候选稿段落 + 标注 + 规律档案 + 证据摘录情况 + 待提炼数 + 模型路由
POSTmarks存某篇某段的 好/坏/取消
POSTevidence原文收录进「证据摘录.md」,档案只刷新自动块
POSTdistill待提炼原文 → 一次辅助模型调用 → 规律条目(待采纳)
POSTrules采纳规律写进档案(幂等),水位推到这批标注
POSTprofile保存界面上改过的档案正文
GETevidence-text读证据摘录正文(展开看原文时才拉)
GETdiscover推荐目录(含篇数、相对路径;5 秒短缓存)
POSTannotate给一批目录回候选稿篇数,供浏览列表打徽标
GET/POSTproject认出/确认作品根(set / clear / detect),检测结果缓存 30 秒
GETworkspace章节看板:每章字数/轮次/标注/批注/写作包/评分/张力/体检问题 + 阶段进度(不带正文)
POSTchapter单章详情:正文段落 + 批注 + 本章设定 + 写作包情况 + 待采纳微调
POSTsetup读/写「本章设定」(写作包的第 ① 节)
POSTannotation批注加/改/删(同段同类型重复提交=更新,不堆两条)
POSTpack生成写作包(save:false 只预览不落盘),回逐节字数与预算表
POSTrevise按批注生成微调稿(只改被批注的段落 + 逐字核对 + 落盘前后对照)
POSTrevise-apply采纳写回正文(先备份原稿、批注标为已处理、核对不过就拒绝)
POSTtension手工标本章张力(1–5,0 表示取消)
POSTcheck账目体检 + 情节体检(refresh:true 强制重算,否则复用 10 分钟缓存)
POSTstage阶段状态 / 设为当前 / 起草(模型)/ 写入产物
GET/POSTrule-evidence规律来源表;action:'backfill' 补齐老档案的来源(有模型路由时走模型判断)
POSTnewchapter开新章:status 现状与建议章号 / directions 要方向 / draft 写第 i 篇候选 / merge 合并选中段落 / finalize 写入正文(原稿先备份)

改代码后怎么生效

  • 浏览器半区 lib/client.js:dsh 按请求现读文件下发(cache-control: no-cache), 刷新页面即生效,不必重启。
  • 宿主半区 lib/index.js:在 dsh 启动时装载,必须重启 dsh 才生效。 客户端对"宿主半区还没有新接口"做了降级:discover 拿到非 JSON(SPA 的 HTML) 一律当空数组,推荐区显示"自己挑",不会卡在转圈。

测试

都不需要浏览器、也不需要起 dsh:假 ctx 挂上真插件,真跑一遍。

npm install   # 开发依赖 + peer 依赖(部署里这些 peer 由 profile 提供)
npm test      # 7 个文件、580+ 断言,全绿才放行

跑单个文件:node test/workbench.test.mjs(0.2 新增能力)、node test/plot.test.mjs、 node test/ledger.test.mjs、node test/pipeline.test.mjs。

刚 clone 下来还没装依赖时不会甩一堆报错:静态守卫照跑,其他会打印「跳过:先 npm install」并以 0 退出。

  • host-api.test.mjs:把插件注册的路由放到真实回环端口上打,覆盖标注闭环、 推荐目录、篇数徽标、档案三层产物、旧档案迁移、提炼链路(用假 LLM 验输入边界与 输出解析)、未配置/停用等分支。
  • client-render.test.mjs:借成对的 react / react-dom 把 client.js 的组件 真渲染成 HTML,断言选择器四个入口、隐藏目录过滤、面包屑省略、档案面板各分支; 纯逻辑(路径缩写、最近使用去重、discover 降级)走 exports.__internals 断言。 档案面板用例是"真宿主生成档案 → 真渲染",不是替身数据。
  • workbench.test.mjs(0.2):三层都打——纯函数(作品识别、批注核对、写作包组装)、 宿主接口(看板/单章/批注/写作包/张力/体检/阶段/批注式微调端到端)、 客户端面板(看板、情节、阶段、批注、微调、写作包按 props 真渲染)。 微调用例会断言"只有被批注的那一段变了,其余段落逐字相同",并验原稿备份与拒绝重复采纳。
  • ledger.test.mjs / plot.test.mjs / pipeline.test.mjs:账目体检、情节诊断、阶段门禁的 纯函数单测(各 100+ 断言),都带"本机有真实作品就读一遍、没有就跳过"的只读用例。

两个测试都不依赖任何本机路径:作品目录由 test/fixtures.mjs 临时造(含候选稿、 归档目录等),写操作全在临时目录里,跑完就删。找不到成对的 react 时渲染用例会打印 「跳过」并以 0 退出,不给别人一堆看不懂的红;npm install 装上 devDependencies (或用 DSH_REACT_ROOT=)即可跑全。

目录结构

dsh-novel-craft-plugin/
├── package.json         # dsh.bundle.patch + dsh.client.platform=web(插件与 skill 的分水岭)
├── cordis.patch.yml     # 安装时把插件行插进 profile 组合
├── lib/index.js         # 宿主半区:settings + 回环 HTTP API + 提炼/微调/起草调用
├── lib/client.js        # 浏览器半区:★ 工作台本体(抽卡 / 章节 / 情节 /