dsh-novel-writer 5.0.0 → 5.1.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.en.md CHANGED
@@ -1,4 +1,4 @@
1
- # 📚 dsh-novel-writer — on-device style checkup for novel writers
1
+ # 📚 dsh-novel-writer — an on-device writing workstation for novel writers
2
2
 
3
3
  English | [中文](./README.md)
4
4
 
@@ -10,21 +10,41 @@ English | [中文](./README.md)
10
10
  [![GitHub stars](https://img.shields.io/github/stars/siweina/dsh-novel-writer.svg?style=flat-square&color=orange)](https://github.com/siweina/dsh-novel-writer/stargazers)
11
11
  [![siweina/dsh-novel-writer MCP server](https://glama.ai/mcp/servers/siweina/dsh-novel-writer/badges/score.svg)](https://glama.ai/mcp/servers/siweina/dsh-novel-writer)
12
12
 
13
- **18 tools that turn "my writing drifted" into numbers you can act on.**
14
- Sentence, emotion and style-baseline analysis all run **on your machine**: a 24MB Chinese model ships with the package,
13
+ **18 tools covering the whole loop: gather inputs before you write → quantify & self-check → work a prioritised fix list → audit the cross-chapter structure.**
14
+ One call before writing gathers **16 inputs** (missing one is how style drift starts); after you write, you don't just get numbers — you get a **priority-ordered to-do list** (line-anchored original text, current vs. target values, reference passages, **direction only, never ghost-written prose**); across chapters you can finally see "the thread snapped, a character vanished, a hook was forgotten".
15
+ Sentence, emotion and semantic analysis all run **on your machine**: a 24MB Chinese model ships with the package,
15
16
  **zero API cost, your manuscript never leaves the device**. Built for DeepSeek Harness (DSH); the same engine is also
16
17
  exposed as a **stdio MCP server** for Claude Desktop / Cursor.
17
18
 
18
- [Install](#install) · [60-second start](#60-second-start) · [See the output](#see-the-output) · [The 18 tools](#provided-tools-18) · [MCP server](#mcp-server-usable-outside-dsh)
19
+ [Install](#install) · [The writing loop](#the-writing-loop-end-to-end) · [60-second start](#60-second-start) · [See the output](#see-the-output) · [The 18 tools](#provided-tools-18) · [MCP server](#mcp-server-usable-outside-dsh)
19
20
 
20
21
  ---
21
22
 
23
+ ## The writing loop (end to end)
24
+
25
+ | Step | What you say | Tool | What you get |
26
+ |---|---|---|---|
27
+ | ① **Gather** | "gather material for the next chapter" | `novel_chapter_brief` | **One call, 16 inputs**: previous chapter's closing text (hand-off) / its hook / this chapter's outline direction / relevant character cards / open plot threads with **how long they've been open** / worldview wording rules + banned words / six-metric baseline / reference passages & sentence skeletons / previous chapter's self-check / avoid-list / writing checklist — anything missing is reported in `degraded` with the reason and the fix |
28
+ | ② **Write** | "write chapter N" | `novel_new_chapter` · `write` | The new chapter file (with the baseline μ attached and the reference pack); the anchors are for **calibrating feel only** (never copy the skeletons), the numbers are for verifying afterwards |
29
+ | ③ **Self-check** | "check chapter N" | `novel_style_check` · `novel_sentence_analysis` | Similarity + deviation list + six-metric comparison (in band ✓ / out of band ⚠). **Only fix when the deviation is obvious (≥2× tolerance) or the passage genuinely reads wrong** — a single dimension drifting slightly is normal variance, don't rewrite to please the numbers |
30
+ | ④ **Revise** | "turn chapter N into a fix list" | `novel_fix_plan` | A to-do list sorted by severity → effort → line (capped at **12 items** by default): line-anchored text, current/target values, anchors, rewrite direction; **work it in batches** (merge same-paragraph items) and run `verify` at most once |
31
+ | ⑤ **Audit** | "show me the book's structure" | `novel_plot { action: "graph" }` | Plot-thread spans / consecutive character absences / largest thread gap / timeline order / outline-vs-body drift |
32
+
33
+ > **Everything local, read-only by default**: the brief and the structure view **write no files at all**; the fix desk writes only its own list file, **never touches your prose and never writes prose for you**; semantic search and emotion analysis cost zero tokens.
34
+
35
+ > **Token-saving mode (new in v5.1.0)**: the sidebar's **Lean workflow** switch (or `novel_sentence_config` with `leanWorkflow=true`) keeps exactly one rule — **call tools on demand**: no automatic self-checks, no re-checking fix lists, no registry updates; report tools return the brief form unless you explicitly pass `brief` (style report: 181 chars vs 1725 for the full one).
36
+ >
37
+ > **v5.1.0 cost & feel fixes**: v5.0.0 had turned "always run a self-check after writing + the closing trio" and "mark every item + verify repeatedly" into mandatory checklists — one chapter went from 3 tool calls to 6, and revising took 30+. Together with "build sentences to the skeletons" and "fix any out-of-band dimension", that made prose stiff. This release makes all of it **on demand**: a chapter now takes **2 calls and up**, the fix-list cap dropped **40 → 12** with only the worst paragraph per dimension, and it now says plainly that **out-of-band ≠ defect** (keep lyrical / psychological / elliptical passages when they read fine — `mark` them `skip`).
38
+
22
39
  ## What problem does it solve
23
40
 
24
41
  | Your pain | What you get here |
25
42
  |---|---|
26
43
  | "This passage I just wrote doesn't sound like me" | **Six-metric style baseline**: μ±σ of the original measured per chapter across syntactic complexity / modifier density / abstraction / action density / hedging / gap index; a new chapter is compared dimension by dimension and flagged ⚠ when out of band |
27
44
  | "The AI says my style changed but can't say where" | **Style check**: similarity score + a deviation list (which sentence types increased, how far sentence length drifted, whether the dominant emotion changed) |
45
+ | "I have to poke six or seven tools before writing, and I still miss one" | **Chapter brief**: 16 inputs in one call; whatever can't be found is explained in `degraded` — **missing an input is the number one source of style drift and setting contradictions** |
46
+ | "I can see the chapter has problems but not which one to fix first" | **Fix plan**: to-dos sorted by severity → effort → line, each with line-anchored text, current vs. target values, a reference passage and a rewrite direction; re-checkable in three states after editing |
47
+ | "A thread snapped, a character vanished, a hook was forgotten — and it only shows up 50 chapters later" | **Structure view**: cross-chapter plot-thread spans, consecutive character-absence intervals, largest thread gap, timeline order, and outline-vs-body overlap |
28
48
  | "Analysing a novel means paying for API calls" | Semantic search and emotion analysis run **fully local** — zero token cost |
29
49
  | "I planted a plot thread and forgot to pay it off" | **Plot registry**: add / list / scan / done, automatically recording which chapters mention each thread |
30
50
  | "Character settings contradict each other" | **Five settings tables + continuity audit** (bridge / OOC / outline drift) |
@@ -55,7 +75,14 @@ appears in the sidebar.
55
75
  mkdir -p novels/my-novel # put chapter files inside (第01章.md, 第02章.md, …)
56
76
  ```
57
77
 
58
- Then just ask in chat: **"run novel_style_report on my novel"** and you get:
78
+ Then ask, in order (this is one complete chapter):
79
+
80
+ 1. **"gather material for the next chapter"** → `novel_chapter_brief` returns all 16 inputs in one call (it does the work of 6–8 tools, and **cannot miss one**)
81
+ 2. **"write chapter 7"** → write by the returned anchors and skeletons; `novel_new_chapter` attaches the baseline μ
82
+ 3. **"check chapter 7"** → `novel_style_check` returns similarity, a deviation list and the six-metric comparison
83
+ 4. **"turn chapter 7 into a fix list"** → `novel_fix_plan` returns the prioritised to-dos; run `verify` afterwards to re-check the three states
84
+
85
+ To just look at the baseline first, ask **"run novel_style_report on my novel"** and you get:
59
86
 
60
87
  ```text
61
88
  全书 1329 字:六维基线 μ=句法复杂度:2.3 修饰密度:35.6 抽象度:0.5 动作密度:101.7 不确定性:2.1 留白指数:7.0
@@ -66,6 +93,38 @@ Then just ask in chat: **"run novel_style_report on my novel"** and you get:
66
93
 
67
94
  ## See the output
68
95
 
96
+ **Chapter brief** (`novel_chapter_brief`, one call before writing):
97
+
98
+ ```text
99
+ 目标:第 8 章《(无标题)》(尚未创建,文件名推导为 第08章.md)
100
+
101
+ 【上一章结尾原文·承接口】…(previous chapter's last 300 / 600 chars, per budget)
102
+ 【待回收伏笔】
103
+ - [mu5kjla…] 琥珀色齿轮怀表的来历与停摆的指针(high|第 1 章埋下,已过 6 章)
104
+ 【相关人物】- 林昭:守码头的女人,父亲失踪后回到旧宅
105
+ 【世界观用语】欧式中世纪沿海城邦;点烛不烧香|禁用:上香、烧香、时辰、老夫
106
+ 【风格基线】complexity μ=2.42 modifierDensity μ=22.2 abstractDensity μ=10.06 …
107
+ 【原著锚段·照这个味道写】[对话] … [心理] …
108
+ 【上一章自检】第 7 章六维对照:全部维度在容差带内 ✓
109
+ 【本章禁用清单】- 禁词:上香(建议改用:点烛)
110
+ 【开写清单】□ 先读锚段再动笔 □ 承接上一章结尾 □ …(共 8 步)
111
+ 【降级/提示】- 钩子记录里没有第 7 章的钩子(上一章钩子未回填)
112
+ ```
113
+
114
+ **Fix plan** (`novel_fix_plan`, diagnostics turned into an ordered to-do list):
115
+
116
+ ```text
117
+ 改稿台:共 13 项待办(严重度降序 → 难度升序)。抽象度过高 ×3、衔接缺失 ×1、禁用词 ×3、语用不符 ×5、句式偏离 ×1。
118
+ 备注:留白指数 虽出带但差值 8.36 < 门槛 12,已按「无量级差异」忽略 ← the absolute-magnitude gate: no false positives
119
+
120
+ - [抽象度过高] 严重度 5 / 难度 2(第 5 行)
121
+ id:fix-abstract-5-c2c156d4
122
+ 现状:abstractDensity 47.9 目标:0.55~6.95
123
+ 原句:林昭大概说不清那种感觉。她隐隐觉得…
124
+ 方向:抽象度偏离:全章 47.9(基线 3.75,偏差 +1177.3%),本段 74.38。这句抽象词过密,改成具体动作或物件。
125
+ 锚段:林昭把它捏在掌心,翻过来看背面。背面刻着一行小字,被磨得只剩半边。她认出了父亲的名字。
126
+ ```
127
+
69
128
  **Style check** (new chapter vs. book baseline):
70
129
 
71
130
  ```text
@@ -104,19 +163,19 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
104
163
 
105
164
  ## Features
106
165
 
107
- 1. **Style portrait report** (`novel_style_report`): 6-dimension measurement — style fingerprint / high-frequency lexicon / genre-theme / emotion quantification / 12-axis vibe spectrum / semantic style distance. **Measurement-judgment separation**: the plugin only reports numbers, never labels; AI judgment can be saved back to `.novel-writer/style-reports/` for consistent continuation writing.
108
- 2. **12-axis vibe spectrum**: nightmare / angst / heartwarming / fluff / tearjerker / dark / mystery / blaze / absurd / lonesome / aesthetic / sensual — with traceable evidence, 0 token.
109
- 3. **Local semantic engine**: bge-small-zh Chinese model (24MB, shipped with the plugin) local CPU inference — `novel_semantic_search` finds semantically related passages with natural language (with chapter location), semantic style comparison, semantic implicit emotion; lazy loading + graceful fallback.
110
- 4. **Sentence-pattern analysis**: 9 categories, arrangement patterns, rhythm, emotion curve, style fingerprint + guidance, with cache & report export.
111
- 5. **Emotion purification & quantification**: strong/weak emotion-word grading, pollution detection, caveat warning + AI re-verification; Valence sliding window → variance V / delta Δ / conflict index C + implicit imagery carriers.
112
- 6. **Worldview & pragmatics detection**: auto cultural-baseline detection with confidence; speechStyle title/honorifics/rituals/tone norms; genre & theme + webnovel signals.
113
- 7. **Writing toolkit**: plot tracking / five settings tables (characters·locations·items·timeline·worldview) / chapter summaries / continuity audit / batch import / style check / continuation writing.
114
- 8. **Per-tool UI toggles**: 「写作助手功能」 ("Writing Assistant") sidebar panel (master + grouped tool toggles + feature toggles), plain-language labels, data-dir usage & semantic-engine status display.
115
- 9. **Style Baseline**: Six writing metrics (syntactic complexity / modifier density / abstraction / action density / hedging / gap index) + per-chapter μ±σ baseline band; `novel_style_report` outputs the band, `novel_style_check` compares new chapters (in-band ✓ / out-of-band ⚠); per-metric ±% tolerance configurable in the sidebar (**recommended = 1.5× σ of the book's chapter variance**, rounded, clamped to ±10%~100%; leave blank to use recommended) — free theme, writing style kept inside the band.
116
- 10. **Writing sentinels**: `novel_continuity_check` extended — ①**bridge check** (`chapter`: time jumps / semantic distance / character continuity / hook handoff, with quoted evidence) ②**OOC check** (`ooc`: per-character emotion baseline deviation) ③**outline drift** (`outline`: direction vs body keyword overlap); **brief mode** for report tools.
117
- 11. **Original mode & creation files**: fill in creation settings in the sidebar (worldview/characters/forbidden/main conflict/genre/extras, blank = model decides, per-book profile library); novel_outline maintains creation files (bible/characters/outline/hooks/status), enforcing the bible → outline → hook chain with dynamic batches (10→20→30 chapters) to prevent plot jumps and OOC.
118
- 12. **Experience & stats**: main panel **library stats** (per-book chapters/total chars/7-day active chars, 🔥 green), **🎬 demo** (built-in sample, no files, runs the 6-dim baseline), **📊 report history** (analysis/style-reports browsing); actionable error hints; slimmer tool descriptions.
119
- 13. **Writing-desk trio**: `novel_chapter_brief` (**chapter brief**) — **one call** before you start writing gathers every input (previous-chapter hand-off / chapter direction / relevant characters / open plot threads / wording rules / style baseline / anchors & skeletons / avoid-list / checklist); anything unavailable lands in `degraded` with the reason; `novel_fix_plan` (**fix plan**) — turns style diagnostics into a priority-ordered to-do list (line-anchored original text + current vs. target values + reference passages + rewrite direction) with `plan` / `verify` / `mark` and a three-state re-check, **direction only, never generates prose**; `novel_plot { action: "graph" }` (**structure view**) — plot-thread spans / consecutive character absences / thread gaps / timeline order / outline-vs-body overlap. Plus **scene-scoped prompts** (general / writing / revising / auditing / setup; `general` is the old behaviour).
166
+ 1. **The writing-desk trio**: `novel_chapter_brief` (**chapter brief**) — **one call** before you start writing gathers every input (previous-chapter hand-off / chapter direction / relevant characters / open plot threads / wording rules / style baseline / anchors & skeletons / avoid-list / checklist; two `budget` modes: compact / full); anything unavailable lands in `degraded` with the reason; read-only, writes nothing. `novel_fix_plan` (**fix plan**) — turns style diagnostics into a priority-ordered to-do list (line-anchored original text + current vs. target values + reference passages + rewrite direction) with `plan` / `verify` / `mark` and a three-state re-check, **direction only, never generates prose**. `novel_plot { action: "graph" }` (**structure view**) — plot-thread spans / consecutive character absences / thread gaps / timeline order / outline-vs-body overlap. Plus **scene-scoped prompts** (general / writing / revising / auditing / setup; `general` is the old behaviour).
167
+ 2. **Style portrait report** (`novel_style_report`): 6-dimension measurement — style fingerprint / high-frequency lexicon / genre-theme / emotion quantification / 12-axis vibe spectrum / semantic style distance. **Measurement-judgment separation**: the plugin only reports numbers, never labels; AI judgment can be saved back to `.novel-writer/style-reports/` for consistent continuation writing.
168
+ 3. **12-axis vibe spectrum**: nightmare / angst / heartwarming / fluff / tearjerker / dark / mystery / blaze / absurd / lonesome / aesthetic / sensual — with traceable evidence, 0 token.
169
+ 4. **Local semantic engine**: bge-small-zh Chinese model (24MB, shipped with the plugin) local CPU inference — `novel_semantic_search` finds semantically related passages with natural language (with chapter location), semantic style comparison, semantic implicit emotion; lazy loading + graceful fallback.
170
+ 5. **Sentence-pattern analysis**: 9 categories, arrangement patterns, rhythm, emotion curve, style fingerprint + guidance, with cache & report export.
171
+ 6. **Emotion purification & quantification**: strong/weak emotion-word grading, pollution detection, caveat warning + AI re-verification; Valence sliding window → variance V / delta Δ / conflict index C + implicit imagery carriers.
172
+ 7. **Worldview & pragmatics detection**: auto cultural-baseline detection with confidence; speechStyle title/honorifics/rituals/tone norms; genre & theme + webnovel signals.
173
+ 8. **Writing toolkit**: plot tracking / five settings tables (characters·locations·items·timeline·worldview) / chapter summaries / continuity audit / batch import / style check / continuation writing.
174
+ 9. **Per-tool UI toggles**: 「写作助手功能」 ("Writing Assistant") sidebar panel (master + grouped tool toggles + feature toggles + prompt mode/scene/**lean workflow**), plain-language labels, data-dir usage & semantic-engine status display.
175
+ 10. **Style Baseline**: Six writing metrics (syntactic complexity / modifier density / abstraction / action density / hedging / gap index) + per-chapter μ±σ baseline band; `novel_style_report` outputs the band, `novel_style_check` compares new chapters (in-band ✓ / out-of-band ⚠); per-metric ±% tolerance configurable in the sidebar (**recommended = 1.5× σ of the book's chapter variance**, rounded, clamped to ±10%~100%; leave blank to use recommended) — free theme, writing style kept inside the band.
176
+ 11. **Writing sentinels**: `novel_continuity_check` extended — ①**bridge check** (`chapter`: time jumps / semantic distance / character continuity / hook handoff, with quoted evidence) ②**OOC check** (`ooc`: per-character emotion baseline deviation) ③**outline drift** (`outline`: direction vs body keyword overlap); **brief mode** for report tools.
177
+ 12. **Original mode & creation files**: fill in creation settings in the sidebar (worldview/characters/forbidden/main conflict/genre/extras, blank = model decides, per-book profile library); novel_outline maintains creation files (bible/characters/outline/hooks/status), enforcing the bible → outline → hook chain with dynamic batches (10→20→30 chapters) to prevent plot jumps and OOC.
178
+ 13. **Experience & stats**: main panel **library stats** (per-book chapters/total chars/7-day active chars, 🔥 green), **🎬 demo** (built-in sample, no files, runs the 6-dim baseline), **📊 report history** (analysis/style-reports browsing); actionable error hints; slimmer tool descriptions.
120
179
 
121
180
  ---
122
181
 
@@ -189,7 +248,7 @@ start the server with `--allow-external-src`. Details: [mcp/README.md](./mcp/REA
189
248
 
190
249
  ## Data Directory
191
250
 
192
- Under `<library-root>/.novel-writer/`: `plots` / `settings` / `summaries` / `analysis` / `audits` / `embedding` / `style-reports`.
251
+ Under `<library-root>/.novel-writer/`: `plots` / `settings` / `summaries` / `analysis` / `audits` (continuity audits + fix-plan lists) / `embedding` / `style-reports`.
193
252
 
194
253
  ---
195
254
 
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # 📚 dsh-novel-writer — 给网文作者的本地章节体检
1
+ # 📚 dsh-novel-writer — 给网文作者的本地写作工作台
2
2
 
3
3
  [English](./README.en.md) | 中文
4
4
 
@@ -10,20 +10,40 @@
10
10
  [![GitHub stars](https://img.shields.io/github/stars/siweina/dsh-novel-writer.svg?style=flat-square&color=orange)](https://github.com/siweina/dsh-novel-writer/stargazers)
11
11
  [![siweina/dsh-novel-writer MCP server](https://glama.ai/mcp/servers/siweina/dsh-novel-writer/badges/score.svg)](https://glama.ai/mcp/servers/siweina/dsh-novel-writer)
12
12
 
13
- **18 个工具,把"文风跑偏"变成可量化的数字。**
14
- 句式 / 情感 / 风格基线全部**在本地算**:24MB 中文模型随包分发,**零 API 花费、正文不出本机**。
13
+ **18 个工具,覆盖「动笔前取材料 → 写完量化自检 → 改稿按优先级复测 → 跨章结构体检」整条流程。**
14
+ 动笔前**一次调用取齐 16 项材料**(漏读一样就漂移);改稿不再只丢给你一堆数字,而是**按严重度排好的待办**(带原句行号、当前值与目标值、原著锚段,**只给方向不代写**);跨章层面能查「线断了、人丢了、伏笔忘了」。
15
+ 句式 / 情感 / 语义全部**在本地算**:24MB 中文模型随包分发,**零 API 花费、正文不出本机**。
15
16
  为 DeepSeek Harness(DSH)打造;同一套能力也可作为 **MCP 服务器**给 Claude Desktop / Cursor 使用。
16
17
 
17
- [安装](#安装) · [60 秒上手](#60-秒上手) · [看看输出](#看看输出长什么样) · [18 个工具](#提供的工具18-个) · [MCP 服务器](#mcp-服务器非-dsh-用户也能用)
18
+ [安装](#安装) · [写作流程](#写作流程一条能走完的闭环) · [60 秒上手](#60-秒上手) · [看看输出](#看看输出长什么样) · [18 个工具](#提供的工具18-个) · [MCP 服务器](#mcp-服务器非-dsh-用户也能用)
18
19
 
19
20
  ---
20
21
 
22
+ ## 写作流程(一条能走完的闭环)
23
+
24
+ | 步骤 | 你说什么 | 用哪个工具 | 拿到什么 |
25
+ |---|---|---|---|
26
+ | ① **取材料** | 「给下一章取材料」 | `novel_chapter_brief` | **一次调用**取齐 16 项输入:上一章结尾原文(承接口)/ 上一章钩子 / 本章大纲方向 / 相关人物卡 / 待回收伏笔(含**埋了多久**)/ 世界观用语规范与禁词 / 六维基线 / 原著锚段与句式骨架 / 上一章自检结论 / 禁用清单 / 开写清单——缺哪项、为什么、怎么补写在 `degraded` 里 |
27
+ | ② **动笔** | 「写第 N 章」 | `novel_new_chapter` · `write` | 新章文件(自动附基线 μ 摘要与原著锚包);锚段只用来**校准语感**(不照骨架造句),数字只做事后校验 |
28
+ | ③ **自检** | 「自检这一章」 | `novel_style_check` · `novel_sentence_analysis` | 相似度 + 偏差清单 + 六维对照(带内 ✓ / 出带 ⚠)。**只有偏离明显(≥2 倍容差)或读起来确实别扭时才改**——单一维度轻微出带属正常波动,不要为对齐数字改文 |
29
+ | ④ **改稿** | 「把这章排成改稿清单」 | `novel_fix_plan` | 按「严重度 → 难度 → 行号」排序的待办(默认上限 **12 条**),每条带原句行号、当前值/目标值、锚段与改写方向;**分批改**(同段同类合并),改完最多 `verify` 一次 |
30
+ | ⑤ **跨章体检** | 「看看全书结构」 | `novel_plot { action: "graph" }` | 伏笔埋设跨度 / 人物连续缺席区间 / 剧情线最大空档 / 时间线顺序 / 大纲方向与正文的偏离 |
31
+
32
+ > **全程本地、只读优先**:开写包与结构视图**不写任何文件**;改稿台只写自己的清单文件、**从不改正文,也不代写正文**;语义检索与情感分析零 token。
33
+
34
+ > **省 token 模式(v5.1.0 新增)**:侧边栏「精简工作流」开关(或 `novel_sentence_config` 传 `leanWorkflow=true`)打开后只保留一条原则——**工具按需调用**:不主动跑自检、不复测清单、不登记伏笔/钩子/摘要;报告类工具未显式传 `brief` 时默认走精简输出(风格画像实测 181 字符 vs 完整 1725 字符)。
35
+ >
36
+ > **v5.1.0 成本与手感修复**:v5.0.0 曾把「写完必须自检 + 收尾三件」「改稿逐条 mark + 反复 verify」写成必做清单,一章从 3 次调用涨到 6 次、改稿要 30+ 次;同时"照骨架造句"与"任一维度出带必改"会让文章越改越僵。本版全部改回**按需**:写一章 **2 次调用起**,改稿清单上限 **40 → 12 条**、同一维度只报最严重的 1 段,并明确了**出带不等于缺陷**(抒情/心理/留白段读起来不别扭就保留,可用 `mark` 标 `skip`)。
37
+
21
38
  ## 它解决什么问题
22
39
 
23
40
  | 你的困扰 | 这里给的答案 |
24
41
  |---|---|
25
42
  | "我续写的这段,读起来不像我自己写的" | **文笔六维基线**:从句法复杂度 / 修饰密度 / 抽象度 / 动作密度 / 不确定性 / 留白指数六个维度算出原著的 μ±σ,新章逐维对照,出带标 ⚠ |
26
43
  | "AI 说文风变了,但说不清哪儿变了" | **风格自检**:相似度 + 偏差清单(哪类句式多了、句长偏了多少、主导情绪有没有换) |
44
+ | "动笔前要翻六七个工具,还老漏读一样" | **开写包**:一次调用取齐 16 项材料;取不到的在 `degraded` 里说明缺什么、怎么补——**漏读正是文风漂移与设定矛盾的头号来源** |
45
+ | "知道这章有问题,但不知道先改哪句" | **改稿台**:按「严重度 → 难度 → 行号」排好待办,每条给原句行号 / 当前值与目标值 / 原著锚段 / 改写方向;改完可三态复测 |
46
+ | "线断了、人丢了、伏笔忘了——写到后面才崩" | **结构视图**:跨章算出伏笔埋设跨度、人物连续缺席区间、剧情线最大空档、时间线顺序、大纲与正文的偏离 |
27
47
  | "分析小说要花钱调 API" | 语义检索与情感分析**全本地推理**,零 token 花费 |
28
48
  | "伏笔埋了忘了收" | **伏笔登记表**:add / list / scan / done,自动记录每条伏笔在哪些章被提到 |
29
49
  | "人物设定前后打架" | **设定五张表 + 连贯性审计**(衔接 / OOC / 大纲走偏三件套) |
@@ -53,7 +73,14 @@ dsh plugin --profile web add github:siweina/dsh-novel-writer#main
53
73
  mkdir -p novels/我的小说 # 把章节文件放进去(第01章.md、第02章.md …)
54
74
  ```
55
75
 
56
- 然后在对话里说:**"用 novel_style_report 给我的小说做一次风格画像"**,你会拿到:
76
+ 然后在对话里依次说(这就是完整的一章):
77
+
78
+ 1. **「给下一章取材料」** → `novel_chapter_brief` 一次给齐 16 项输入(等于替你跑了 6~8 个工具,且**不会漏读**)
79
+ 2. **「写第 7 章」** → 照返回的锚段与骨架写;`novel_new_chapter` 建文件时还会附上基线 μ
80
+ 3. **「自检第 7 章」** → `novel_style_check` 给出相似度、偏差清单与六维对照
81
+ 4. **「把第 7 章排成改稿清单」** → `novel_fix_plan` 给出按优先级排好的待办;改完再 `verify` 复测三态
82
+
83
+ 只想先看看家底,就说 **「用 novel_style_report 给我的小说做一次风格画像」**:
57
84
 
58
85
  ```text
59
86
  全书 1329 字:六维基线 μ=句法复杂度:2.3 修饰密度:35.6 抽象度:0.5 动作密度:101.7 不确定性:2.1 留白指数:7.0
@@ -62,6 +89,42 @@ mkdir -p novels/我的小说 # 把章节文件放进去(第01章.md、第0
62
89
 
63
90
  ## 看看输出长什么样
64
91
 
92
+ **开写包**(`novel_chapter_brief`,动笔前一次调用):
93
+
94
+ ```text
95
+ 目标:第 8 章《(无标题)》(尚未创建,文件名推导为 第08章.md)
96
+
97
+ 【上一章结尾原文·承接口】…(上一章末尾 300 / 600 字,按 budget 档位)
98
+ 【待回收伏笔】
99
+ - [mu5kjla…] 琥珀色齿轮怀表的来历与停摆的指针(high|第 1 章埋下,已过 6 章)
100
+ - [mu5kjlb…] 海图上红铅笔圈的礁区坐标(high|第 3 章埋下,已过 4 章)
101
+ 【相关人物】- 林昭:守码头的女人,父亲失踪后回到旧宅 - 沈砚:随船出海的人
102
+ 【世界观用语】欧式中世纪沿海城邦;点烛不烧香|禁用:上香、烧香、时辰、老夫
103
+ 【风格基线】complexity μ=2.42 modifierDensity μ=22.2 abstractDensity μ=10.06 actionDensity μ=122.45 …
104
+ 【原著锚段·照这个味道写】[对话] … [心理] …
105
+ 【上一章自检】第 7 章六维对照:全部维度在容差带内 ✓
106
+ 【本章禁用清单】- 禁词:上香(建议改用:点烛) - 禁词:时辰(建议改用:钟点)
107
+ 【开写清单】□ 先读锚段再动笔 □ 承接上一章结尾 □ …(共 8 步)
108
+ 【降级/提示】- 钩子记录里没有第 7 章的钩子(上一章钩子未回填)
109
+ ```
110
+
111
+ **改稿台**(`novel_fix_plan`,把诊断变成排好序的待办):
112
+
113
+ ```text
114
+ 改稿台:共 13 项待办(严重度降序 → 难度升序)。抽象度过高 ×3、衔接缺失 ×1、禁用词 ×3、语用不符 ×5、句式偏离 ×1。
115
+ 备注:留白指数 虽出带但差值 8.36 < 门槛 12,已按「无量级差异」忽略 ← 不误报的绝对量级闸
116
+
117
+ - [抽象度过高] 严重度 5 / 难度 2(第 5 行)
118
+ id:fix-abstract-5-c2c156d4
119
+ 现状:abstractDensity 47.9 目标:0.55~6.95
120
+ 原句:林昭大概说不清那种感觉。她隐隐觉得…
121
+ 方向:抽象度偏离:全章 47.9(基线 3.75,偏差 +1177.3%),本段 74.38。这句抽象词过密,改成具体动作或物件。
122
+ 锚段:林昭把它捏在掌心,翻过来看背面。背面刻着一行小字,被磨得只剩半边。她认出了父亲的名字。
123
+ - [语用不符] 严重度 4 / 难度 2(第 15 行)
124
+ 现状:客套禁词「承蒙」(第 15 行) 目标:避免该类客套表达
125
+ 方向:「承蒙」属世界观说话方式规范里明确不用的客套表达(honorBad)。这是说法层面的替换,不要顺手改剧情。
126
+ ```
127
+
65
128
  **风格自检**(新章 vs 全书基线):
66
129
 
67
130
  ```text
@@ -97,19 +160,19 @@ fixAnchors:3 条原著锚段(对话 / 心理 / 描写各一条,供逐句
97
160
 
98
161
  ## 功能
99
162
 
100
- 1. **风格画像报告**(novel_style_report):6 维测量报告——文风指纹 / 高频词汇 / 题材流派 / 情感量化 / 氛围光谱 12 轴 / 语义风格距离。**测量与判断分离**:插件只报数不贴标签,AI 判断可回传存盘(`.novel-writer/style-reports/`),续写保持风格一致。
101
- 2. **氛围光谱 12 轴**:噩梦感 / 焦虑压抑 / 温馨治愈 / 甜宠日常 / 催泪虐心 / 黑暗残酷 / 悬疑神秘 / 热血激昂 / 荒诞无厘头 / 孤独疏离 / 文艺唯美 / 情欲暧昧——证据链可追溯,0 token。
102
- 3. **本地语义引擎**:bge-small-zh 中文模型(24MB 随插件分发)本地 CPU 推理——`novel_semantic_search` 自然语言搜全书语义相关段落(带章节定位),语义级风格对比、语义隐性情感,懒加载 + 自动回退。
103
- 4. **句式模式分析**:九类句式分布、排列规律、句长节奏、情感曲线、风格指纹与节奏建议,带缓存与报告导出。
104
- 5. **情感净化 + 量化**:强/弱情绪词分级、污染源检测、caveat 预警 + AI 复核;Valence 滑动窗口 → 方差 V / 斜率 Δ / 矛盾指数 C + 隐性意象载体。
105
- 6. **世界观与语用检测**:文化基准自动判断(西/东/混合)+ 置信度;speechStyle 称谓/客套/仪式/语气规范;题材流派 + 网文信号。
106
- 7. **写作辅助全家桶**:伏笔登记表 / 设定五张表(人物·地点·道具·时间线·世界观)/ 章节摘要 / 连贯性审计 / 批量导入 / 风格自检 / 续写辅助。
107
- 8. **全工具 UI 开关**:侧边栏「写作助手功能」面板(总开关 + 工具开关分组 + 功能开关),大白话文案,显示数据目录占用与语义引擎状态。
108
- 9. **风格基线**:文笔六维测量(句法复杂度/修饰密度/抽象度/动作密度/不确定性/留白指数)+ 按章节 μ±σ 基线带;`novel_style_report` 输出基线带,`novel_style_check` 对照新章偏差(带内 ✓ / 出带 ⚠);侧边栏可自定义每维 ±% 容差(**推荐值 = 原著章节波动的 1.5 倍 σ**,自动取整、限 ±10%~100%;输入框留空即用推荐)——原创/续写时主题自由、写法保持在基线带内。
109
- 10. **写作哨兵三件套**:`novel_continuity_check` 扩展——①**衔接检查**(chapter 参数:时间硬跳/语义距离/人物延续/钩子承接四路检测,带原文引用)②**OOC 检测**(ooc 参数:角色情绪基线偏离)③**大纲走偏**(outline 参数:方向行 vs 正文关键词重合);报告工具支持 **brief 精简模式**。
110
- 11. **原创模式与创作资料**:侧边栏填写创作设定(世界观/角色/禁忌/主线/题材/额外要求,留空=模型自定,多书独立设定库);novel_outline 维护创作资料(创作设定/人物/剧情大纲/钩子记录/创作状态卡),原创强制「设定书→大纲→钩子」链,动态批次(10→20→30 章)防剧情跳跃与角色 OOC。
111
- 12. **体验与统计**:主面板**书库统计卡**(每本章数/总字数/近 7 天活跃字数,活跃🔥标绿)、**🎬 体验演示**(内置示例不落盘跑六维基线)、**📊 报告历史**(analysis/style-reports 列表浏览);错误提示带解决步骤;工具说明压缩省 token。
112
- 13. **写作台三件套**:`novel_chapter_brief` **开写包**——动笔前**一次调用**取齐材料(上一章承接口 / 本章方向 / 相关人物 / 待回收伏笔 / 用语规范 / 风格基线 / 锚段与骨架 / 禁用清单 / 开写清单),取不到的材料进 `degraded` 并说明原因;`novel_fix_plan` **改稿台**——把风格诊断变成按优先级排好的待办(原句行号定位 + 当前值与目标值 + 原著锚段 + 改写方向),`plan` / `verify` / `mark` 三态复测,**只给方向、不生成正文**;`novel_plot { action: "graph" }` **结构视图**——伏笔埋设跨度 / 人物连续缺席 / 剧情线空档 / 时间线顺序 / 大纲对照。另有**场景化提示词**(general / writing / revising / auditing / setup,`general` 即旧行为)。
163
+ 1. **写作台三件套**:`novel_chapter_brief` **开写包**——动笔前**一次调用**取齐材料(上一章承接口 / 本章方向 / 相关人物 / 待回收伏笔 / 用语规范 / 风格基线 / 锚段与骨架 / 禁用清单 / 开写清单,两档 `budget`:compact / full),取不到的材料进 `degraded` 并说明原因,只读不写盘;`novel_fix_plan` **改稿台**——把风格诊断变成按优先级排好的待办(原句行号定位 + 当前值与目标值 + 原著锚段 + 改写方向),`plan` / `verify` / `mark` 三态复测,**只给方向、不生成正文**;`novel_plot { action: "graph" }` **结构视图**——伏笔埋设跨度 / 人物连续缺席 / 剧情线空档 / 时间线顺序 / 大纲对照。另有**场景化提示词**(general / writing / revising / auditing / setup,`general` 即旧行为)。
164
+ 2. **风格画像报告**(novel_style_report):6 维测量报告——文风指纹 / 高频词汇 / 题材流派 / 情感量化 / 氛围光谱 12 轴 / 语义风格距离。**测量与判断分离**:插件只报数不贴标签,AI 判断可回传存盘(`.novel-writer/style-reports/`),续写保持风格一致。
165
+ 3. **氛围光谱 12 轴**:噩梦感 / 焦虑压抑 / 温馨治愈 / 甜宠日常 / 催泪虐心 / 黑暗残酷 / 悬疑神秘 / 热血激昂 / 荒诞无厘头 / 孤独疏离 / 文艺唯美 / 情欲暧昧——证据链可追溯,0 token。
166
+ 4. **本地语义引擎**:bge-small-zh 中文模型(24MB 随插件分发)本地 CPU 推理——`novel_semantic_search` 自然语言搜全书语义相关段落(带章节定位),语义级风格对比、语义隐性情感,懒加载 + 自动回退。
167
+ 5. **句式模式分析**:九类句式分布、排列规律、句长节奏、情感曲线、风格指纹与节奏建议,带缓存与报告导出。
168
+ 6. **情感净化 + 量化**:强/弱情绪词分级、污染源检测、caveat 预警 + AI 复核;Valence 滑动窗口 → 方差 V / 斜率 Δ / 矛盾指数 C + 隐性意象载体。
169
+ 7. **世界观与语用检测**:文化基准自动判断(西/东/混合)+ 置信度;speechStyle 称谓/客套/仪式/语气规范;题材流派 + 网文信号。
170
+ 8. **写作辅助全家桶**:伏笔登记表 / 设定五张表(人物·地点·道具·时间线·世界观)/ 章节摘要 / 连贯性审计 / 批量导入 / 风格自检 / 续写辅助。
171
+ 9. **全工具 UI 开关**:侧边栏「写作助手功能」面板(总开关 + 工具开关分组 + 功能开关 + 提示词档位/场景/**精简工作流**),大白话文案,显示数据目录占用与语义引擎状态。
172
+ 10. **风格基线**:文笔六维测量(句法复杂度/修饰密度/抽象度/动作密度/不确定性/留白指数)+ 按章节 μ±σ 基线带;`novel_style_report` 输出基线带,`novel_style_check` 对照新章偏差(带内 ✓ / 出带 ⚠);侧边栏可自定义每维 ±% 容差(**推荐值 = 原著章节波动的 1.5 倍 σ**,自动取整、限 ±10%~100%;输入框留空即用推荐)——原创/续写时主题自由、写法保持在基线带内。
173
+ 11. **写作哨兵三件套**:`novel_continuity_check` 扩展——①**衔接检查**(chapter 参数:时间硬跳/语义距离/人物延续/钩子承接四路检测,带原文引用)②**OOC 检测**(ooc 参数:角色情绪基线偏离)③**大纲走偏**(outline 参数:方向行 vs 正文关键词重合);报告工具支持 **brief 精简模式**。
174
+ 12. **原创模式与创作资料**:侧边栏填写创作设定(世界观/角色/禁忌/主线/题材/额外要求,留空=模型自定,多书独立设定库);novel_outline 维护创作资料(创作设定/人物/剧情大纲/钩子记录/创作状态卡),原创强制「设定书→大纲→钩子」链,动态批次(10→20→30 章)防剧情跳跃与角色 OOC。
175
+ 13. **体验与统计**:主面板**书库统计卡**(每本章数/总字数/近 7 天活跃字数,活跃🔥标绿)、**🎬 体验演示**(内置示例不落盘跑六维基线)、**📊 报告历史**(analysis/style-reports 列表浏览);错误提示带解决步骤;工具说明压缩省 token。
113
176
 
114
177
  ---
115
178
 
@@ -178,7 +241,7 @@ npx -y -p dsh-novel-writer dsh-novel-writer-mcp --root /你的小说库路径
178
241
 
179
242
  ## 数据目录
180
243
 
181
- `<书库根>/.novel-writer/`:`plots`(伏笔)/ `settings`(设定)/ `summaries`(摘要)/ `analysis`(分析报告)/ `audits`(审计)/ `embedding`(语义索引)/ `style-reports`(风格画像)。
244
+ `<书库根>/.novel-writer/`:`plots`(伏笔)/ `settings`(设定)/ `summaries`(摘要)/ `analysis`(分析报告)/ `audits`(连贯性审计 + 改稿清单 `fix-plan-*.json`)/ `embedding`(语义索引)/ `style-reports`(风格画像)。
182
245
 
183
246
  ---
184
247
 
package/lib/brief.js CHANGED
@@ -567,7 +567,10 @@ export async function buildChapterBrief(root, book, chapterArg, opts, exec) {
567
567
  ? "大纲未给方向、上一章钩子未回填:从上一章结尾(anchor)自然接续"
568
568
  : "全书开篇且无大纲方向:按创作设定开篇";
569
569
 
570
- const checklist = ["先读【anchors 锚段】再动笔,按原著味道写;baseline 数字只做写完后的校验"];
570
+ // v5.0.0 修正(降本):清单从"催办单"改成"提醒单"——只有真要调用工具的两条被降级为按需/建议。
571
+ // 实测背景:清单里的"跑 novel_style_check 自检 / 回填钩子"会被模型当成必做步骤,每章固定多出 3 次调用,
572
+ // 而每次调用都要重发整章正文;锚段被要求"按味道与形状写"则会让句式趋同(僵化)。
573
+ const checklist = ["读一遍【anchors 锚段】校准语感——**不要照 skeletons 造句、不要套它们的句式模板**(骨架只在写不出该类型句子时参考);baseline 数字只做写完后的校验"];
571
574
  if (previousHook !== "") checklist.push("本章开头接住上一章钩子:" + clip(previousHook, 30));
572
575
  else if (prevChapter) checklist.push("本章开头承接上一章结尾(原文见 anchor)");
573
576
  if (outlineDirection !== "") checklist.push("按本章大纲方向行推进:" + clip(outlineDirection, 40));
@@ -585,10 +588,10 @@ export async function buildChapterBrief(root, book, chapterArg, opts, exec) {
585
588
  const names = shownCharacters.slice(0, 5).map((c) => c.name + (c.offstage === true ? "(已退场,勿写其出场)" : "")).join("、");
586
589
  checklist.push("本章相关人物:" + names + "——对照其说话方式与关系");
587
590
  }
588
- if (baseline) checklist.push("写完对照六维基线(baseline 的 μ±σ)与锚段,跑 novel_style_check 自检");
589
- if (chapter && typeof chapter.number === "number") {
590
- checklist.push("写完回填钩子:novel_outline action=hook number=" + chapter.number);
591
+ if (baseline) {
592
+ checklist.push("写完**建议**跑一次 novel_style_check;只有相似度明显偏低、或某维偏离超过 2 倍容差时才逐句修正——单一维度轻微出带属正常波动(1.5σ 带宽下常见),**不要为对齐数字改文**");
591
593
  }
594
+ checklist.push("收尾按需,不必每章全套:确属本章新埋的伏笔才 novel_plot add;钩子与摘要在章节稳定后再回填(novel_outline hook / novel_summary)");
592
595
 
593
596
  return {
594
597
  book: safeBook,
package/lib/client.js CHANGED
@@ -200,6 +200,8 @@ var css = `
200
200
  "sc.revising": "改稿",
201
201
  "sc.auditing": "审计",
202
202
  "sc.setup": "建资料",
203
+ "panel.leanWorkflow": "精简工作流",
204
+ "panel.leanWorkflowHint": "不主动跑自检、不复测清单、不登记伏笔/钩子/摘要,只在你要求时调用工具——省 token,适合成本敏感的长篇",
203
205
  "panel.back": "Back",
204
206
  "panel.featuresTitle": "功能开关",
205
207
  "panel.featuresHint": "情感净化预警:剔除感官/爽感词干扰后提示 AI 复核真实情绪;题材与流派检测:detect 时输出主/副题材与设定流派。",
@@ -412,6 +414,8 @@ var css = `
412
414
  "sc.revising": "Revising",
413
415
  "sc.auditing": "Auditing",
414
416
  "sc.setup": "Setup",
417
+ "panel.leanWorkflow": "Lean workflow",
418
+ "panel.leanWorkflowHint": "Skip auto self-checks, re-checks and registry updates — tools run only when you ask. Saves tokens on long books.",
415
419
  "panel.featuresTitle": "Feature switches",
416
420
  "panel.featuresHint": "Emotion caveat: strip physiological/euphoric words and ask the AI to re-check real tone. Genre/theme: output main/secondary theme and setting genre on detect.",
417
421
  "feature.webnovelVibe": "Webnovel signals",
@@ -694,6 +698,8 @@ var css = `
694
698
  systemPromptMode: "brief",
695
699
  // v5.0.0:提示词场景(与档位正交);老宿主不返回该字段 → general,行为与 4.x 一致
696
700
  promptScene: "general",
701
+ // v5.0.0:精简工作流(全局布尔,与档位/场景同级;老宿主不返回该字段 → false,行为与 5.0.0 之前一致)
702
+ leanWorkflow: false,
697
703
  tools: {},
698
704
  file: "",
699
705
  loading: true,
@@ -772,6 +778,8 @@ var css = `
772
778
  systemPromptMode: (stale ? (api.getSnapshot().systemPromptMode || "brief") : (remote.systemPromptMode || "brief")),
773
779
  // v5.0.0:同步场景(刷新路径同样不能漏读,否则场景行显示旧值)
774
780
  promptScene: (stale ? (api.getSnapshot().promptScene || "general") : (remote.promptScene || "general")),
781
+ // v5.0.0:精简工作流(刷新路径同样不能漏读;严格 === true——字符串/数字脏值不当成"开")
782
+ leanWorkflow: (stale ? api.getSnapshot().leanWorkflow === true : remote.leanWorkflow === true),
775
783
  // v3.5.0 R4:请求期间版本未变(用户没操作)才用远端覆盖开关——慢请求不覆盖用户刚切的
776
784
  tools: (stale ? (api.getSnapshot().tools || {}) : (remote.tools || {})),
777
785
  features: (stale ? (api.getSnapshot().features || {}) : (remote.features || {})),
@@ -1644,6 +1652,19 @@ var TOOL_GROUPS = [
1644
1652
  })
1645
1653
  )
1646
1654
  ),
1655
+ // v5.0.0:精简工作流——全局开关,紧挨「提示词档位 / 提示词场景」两行之下、与相邻开关同一套 switch/badge 组件。
1656
+ // 与上一条「提示词场景」的区别:它不受"仅完整档生效"限制(不给 disabled 逻辑),任何时候都可切换;
1657
+ // 打开(true)= 提示词换"按需调用"最简档,关闭(false,默认)= 维持 5.0.0 的自检/收尾/复测清单。
1658
+ el("div", { className: "nwRow " + (state.leanWorkflow ? "nwRowOn" : "nwRowOff") },
1659
+ el("div", { className: "nwRowText" },
1660
+ el("div", { className: "nwRowLabel" }, t("panel.leanWorkflow")),
1661
+ el("div", { className: "nwRowHint" }, t("panel.leanWorkflowHint"))
1662
+ ),
1663
+ el("div", { className: "nwSwitchWrap" },
1664
+ el(Switch, { checked: state.leanWorkflow === true, disabled: state.loading, onChange: function (value) { props.toggle({ leanWorkflow: value }); } }),
1665
+ el("span", { className: "nwBadge " + (state.leanWorkflow ? "nwBadgeOn" : "nwBadgeOff") }, state.leanWorkflow ? t("badge.on") : t("badge.off"))
1666
+ )
1667
+ ),
1647
1668
  (function () {
1648
1669
  var rawOn = f.rawWriting === true;
1649
1670
  return el("div", { className: "nwToolRow" + (rawOn ? " nwToolRowOn" : " nwToolRowRaw"), key: "rawWriting" },
@@ -1953,6 +1974,8 @@ var TOOL_GROUPS = [
1953
1974
  autoAnalyze: revSame ? !!remote.autoAnalyze : snap.autoAnalyze,
1954
1975
  systemPromptMode: revSame ? (remote.systemPromptMode || "brief") : (snap.systemPromptMode || "brief"),
1955
1976
  promptScene: revSame ? (remote.promptScene || "general") : (snap.promptScene || "general"),
1977
+ // v5.0.0:精简工作流(首次加载同样不能漏读,否则刷新后开关显示回关闭态)
1978
+ leanWorkflow: revSame ? remote.leanWorkflow === true : snap.leanWorkflow === true,
1956
1979
  tools: revSame ? (remote.tools || {}) : (snap.tools || {}),
1957
1980
  features: revSame ? (remote.features || {}) : (snap.features || {}),
1958
1981
  plotsDir: remote.plotsDir || "",
@@ -1980,6 +2003,8 @@ var TOOL_GROUPS = [
1980
2003
  autoAnalyze: typeof local.autoAnalyze === "boolean" ? local.autoAnalyze : true,
1981
2004
  systemPromptMode: typeof local.systemPromptMode === "string" ? local.systemPromptMode : "brief",
1982
2005
  promptScene: typeof local.promptScene === "string" ? local.promptScene : "general",
2006
+ // v5.0.0:精简工作流(宿主不可达时的 localStorage 降级路径;与核心一致只认布尔 true)
2007
+ leanWorkflow: local.leanWorkflow === true,
1983
2008
  // v3.5.0 #38:完整恢复本地降级状态
1984
2009
  tools: local.tools || {},
1985
2010
  features: local.features || {},
@@ -2032,6 +2057,8 @@ var TOOL_GROUPS = [
2032
2057
  autoAnalyze: revSame2 ? !!remote.autoAnalyze : !!snap2.autoAnalyze,
2033
2058
  systemPromptMode: revSame2 ? (remote.systemPromptMode || "brief") : (snap2.systemPromptMode || "brief"),
2034
2059
  promptScene: revSame2 ? (remote.promptScene || "general") : (snap2.promptScene || "general"),
2060
+ // v5.0.0:精简工作流(保存成功后回读宿主 next,同样受 revSame2 守卫保护)
2061
+ leanWorkflow: revSame2 ? remote.leanWorkflow === true : snap2.leanWorkflow === true,
2035
2062
  // v3.9.5 修正:保存成功后回读宿主完整 next(tools/features/容差/设定),
2036
2063
  // 前提是期间没有更新的用户操作(rev 未变)——避免服务端钳制/规范化与 UI 长期不一致
2037
2064
  tools: revSame2 && remote.tools ? remote.tools : (snap2.tools || {}),
@@ -2056,7 +2083,7 @@ var TOOL_GROUPS = [
2056
2083
  // v3.5.0 #38:宿主不可达时完整持久化(刷新不丢 tools/features/容差/设定)
2057
2084
  // v4.3.0:补写 systemPromptMode——读路径一直会读它(load 的 local.systemPromptMode),
2058
2085
  // 写路径却从不写 → 宿主不可达时切"关闭",刷新后又变回"精简"(读写不对称)
2059
- localStorage.setItem(STORAGE_KEY, JSON.stringify({ enabled: snapshot.enabled, autoAnalyze: snapshot.autoAnalyze, systemPromptMode: snapshot.systemPromptMode || "brief", promptScene: snapshot.promptScene || "general", tools: snapshot.tools || {}, features: snapshot.features || {}, styleTolerance: snapshot.styleTolerance || null, creationProfile: snapshot.creationProfile || null, creationProfiles: snapshot.creationProfiles || {} }));
2086
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ enabled: snapshot.enabled, autoAnalyze: snapshot.autoAnalyze, systemPromptMode: snapshot.systemPromptMode || "brief", promptScene: snapshot.promptScene || "general", leanWorkflow: snapshot.leanWorkflow === true, tools: snapshot.tools || {}, features: snapshot.features || {}, styleTolerance: snapshot.styleTolerance || null, creationProfile: snapshot.creationProfile || null, creationProfiles: snapshot.creationProfiles || {} }));
2060
2087
  } catch { /* ignore */ }
2061
2088
  };
2062
2089
  controller.setToggle(toggle);
package/lib/core.js CHANGED
@@ -695,7 +695,9 @@ export function formatImport(value) {
695
695
  lines.push("", "</content>");
696
696
  return lines.join("\n");
697
697
  }
698
- export const SENTENCE_ANALYSIS_DEFAULTS = Object.freeze({ enabled: true, autoAnalyze: true, systemPromptMode: "brief", promptScene: "general" });
698
+ // v5.0.0:leanWorkflow=精简工作流(全局布尔开关,默认 false=保持既有强制清单行为)。
699
+ // 它与 promptScene 同级(顶层键,不是 features 功能开关)——写进 features 会被 featureEnabled 的白名单语义污染。
700
+ export const SENTENCE_ANALYSIS_DEFAULTS = Object.freeze({ enabled: true, autoAnalyze: true, systemPromptMode: "brief", promptScene: "general", leanWorkflow: false });
699
701
  export const ALL_TOOLS = Object.freeze([
700
702
  "novel_books", "novel_chapters", "novel_read", "novel_keywords", "novel_new_chapter",
701
703
  "novel_import", "novel_sentence_analysis", "novel_sentence_config", "novel_style_check", "novel_plot",
@@ -951,6 +953,9 @@ export async function readSentenceState() {
951
953
  systemPromptMode: ["off", "brief", "full"].includes(parsed.systemPromptMode) ? parsed.systemPromptMode : SENTENCE_ANALYSIS_DEFAULTS.systemPromptMode,
952
954
  // v5.0.0:提示词场景(与档位正交;老状态文件没有该键 → general,即 4.x 通用工作流 + 本版补入的 7 行新工具指引)
953
955
  promptScene: PROMPT_SCENES.includes(parsed.promptScene) ? parsed.promptScene : SENTENCE_ANALYSIS_DEFAULTS.promptScene,
956
+ // v5.0.0:精简工作流(严格布尔校验:只有真正的 true/false 才采用,"true"/1/null/缺失一律回退 false,
957
+ // 与上面 promptScene 的白名单校验同一风格——不做真值转换,避免老状态文件里的脏值把开关"意外打开")
958
+ leanWorkflow: typeof parsed.leanWorkflow === "boolean" ? parsed.leanWorkflow : SENTENCE_ANALYSIS_DEFAULTS.leanWorkflow,
954
959
  tools: parsed.tools !== null && typeof parsed.tools === "object" ? parsed.tools : {},
955
960
  features: parsed.features !== null && typeof parsed.features === "object" ? parsed.features : {},
956
961
  // v3.0.0:风格基线容差(±%:用户允许低于/高于基线的范围)
@@ -968,7 +973,9 @@ export async function readSentenceState() {
968
973
  export function readSentenceStateSync() {
969
974
  try {
970
975
  const file = stateFilePath();
971
- if (!existsSync(file)) return {};
976
+ // v5.0.0:无状态文件/解析失败时仍给出 leanWorkflow —— 调用方(提示词注入侧)无条件读 st.leanWorkflow,
977
+ // 不能出现"有文件=布尔值、无文件=undefined"两套口径(其余字段保持既有的"缺失即 undefined"语义不动)
978
+ if (!existsSync(file)) return { leanWorkflow: false };
972
979
  const parsed = JSON.parse(readFileSync(file, "utf8"));
973
980
  return {
974
981
  enabled: parsed.enabled !== false,
@@ -976,6 +983,9 @@ export function readSentenceStateSync() {
976
983
  systemPromptMode: ["off", "brief", "full"].includes(parsed.systemPromptMode) ? parsed.systemPromptMode : "brief",
977
984
  // v5.0.0:提示词场景(同步版,供 systemPrompt.section 的 text 回调使用)
978
985
  promptScene: PROMPT_SCENES.includes(parsed.promptScene) ? parsed.promptScene : "general",
986
+ // v5.0.0:精简工作流(同步版,与异步版 readSentenceState 同一严格布尔校验——两版口径必须一致,
987
+ // 否则同一次会话里"注入提示词读到的值"与"UI/tool 读到的值"会互相矛盾)
988
+ leanWorkflow: typeof parsed.leanWorkflow === "boolean" ? parsed.leanWorkflow : false,
979
989
  tools: parsed.tools !== null && typeof parsed.tools === "object" ? parsed.tools : {},
980
990
  features: parsed.features !== null && typeof parsed.features === "object" ? parsed.features : {},
981
991
  // v3.9.5:同步版补齐 styleTolerance(与异步版 readSentenceState 结构对齐)
@@ -986,7 +996,7 @@ export function readSentenceStateSync() {
986
996
  creationProfiles: parsed.creationProfiles !== null && typeof parsed.creationProfiles === "object" ? parsed.creationProfiles : {},
987
997
  lastRoot: typeof parsed.lastRoot === "string" ? parsed.lastRoot : void 0
988
998
  };
989
- } catch { return {}; }
999
+ } catch { return { leanWorkflow: false }; } // v5.0.0:解析失败同样回退默认(理由同上)
990
1000
  }
991
1001
  export const writeQueues = new Map();
992
1002
  export async function atomicWriteJson(file, obj) {
@@ -1047,6 +1057,9 @@ export async function writeSentenceState(patch) {
1047
1057
  ...(["off", "brief", "full"].includes(patch?.systemPromptMode) ? { systemPromptMode: patch.systemPromptMode } : {}),
1048
1058
  // v5.0.0:提示词场景(非法值忽略;general 是显式合法值,用于改回通用档)
1049
1059
  ...(PROMPT_SCENES.includes(patch?.promptScene) ? { promptScene: patch.promptScene } : {}),
1060
+ // v5.0.0:精简工作流(只接受布尔值;非布尔值静默忽略——与上面 promptScene 处理非法枚举值的既有做法一致,
1061
+ // 开关类字段不做隐式转换:字符串 "true"/数字 1 一律当作没传,不写盘、不报错)
1062
+ ...(typeof patch?.leanWorkflow === "boolean" ? { leanWorkflow: patch.leanWorkflow } : {}),
1050
1063
  ...(patch?.tools !== null && typeof patch?.tools === "object" ? { tools: { ...(current.tools ?? {}), ...patch.tools } } : {}),
1051
1064
  ...(typeof patch?.lastRoot === "string" ? { lastRoot: patch.lastRoot } : {}),
1052
1065
  ...(patch?.features !== null && typeof patch?.features === "object" ? { features: { ...(current.features ?? {}), ...patch.features } } : {}),
@@ -1191,6 +1204,8 @@ export function makeStateRoutes(allowLan, config) {
1191
1204
  systemPromptMode: state.systemPromptMode ?? "brief",
1192
1205
  // v5.0.0:提示词场景(侧边栏首页开关读它)
1193
1206
  promptScene: state.promptScene ?? "general",
1207
+ // v5.0.0:精简工作流(侧边栏首页开关读它;老宿主/老状态文件没有该键 → false,UI 显示关闭态)
1208
+ leanWorkflow: state.leanWorkflow ?? false,
1194
1209
  tools: state.tools ?? {},
1195
1210
  features: state.features ?? {},
1196
1211
  // v3.0.0:风格基线容差(±%)
package/lib/fixplan.js CHANGED
@@ -67,11 +67,20 @@ const PLOT_STALE_CHAPTERS = 20;
67
67
  /** v5.0.0:基线章节数少于 3 时 σ 不可靠(小样本),容差下限抬到 25%,避免「只有两章的书」互相误报。
68
68
  * 容差公式与常量已上移到 core.js(buildTolerance / MIN_BASELINE_CHAPTERS)——开写包要用同一口径。 */
69
69
 
70
- /** v5.0.0:同一维度最多归因几个段落(防止「整章抽象度过高」刷出几十条同质待办)。 */
71
- const MAX_PARAGRAPH_ITEMS_PER_METRIC = 3;
70
+ /**
71
+ * v5.0.0 修正(降本):同一维度最多归因 1 个段落(原为多个)。
72
+ * 同一维度归因多个段落会让清单成倍膨胀:模型面对同质待办会倾向「逐段改写」,
73
+ * 一次改稿动辄刷出十几条,既贵(每条都要重发整章上下文),又会让全章语气被抹成同一种(越改越僵)。
74
+ * 只报该维度**最严重**的那 1 段,其余段落的量由 summary 的计数体现——要看全章分布时读计数即可。
75
+ */
76
+ const MAX_PARAGRAPH_ITEMS_PER_METRIC = 1;
72
77
 
73
- /** v5.0.0:单次清单的条数上限(超出按排序截断,summary 会披露截断数)。 */
74
- const DEFAULT_MAX_ITEMS = 40;
78
+ /**
79
+ * v5.0.0 修正(降本):单次清单的条数上限(超出按排序截断,summary 会披露截断数)。
80
+ * 清单是「先改最要紧的」,不是「一次改完」:上限压低后模型不会去追长尾——
81
+ * 被截断的部分在 summary 备注里如实披露,改完这几条重跑清单,下一批自然浮现。
82
+ */
83
+ const DEFAULT_MAX_ITEMS = 12;
75
84
 
76
85
  /** v5.0.0:参与段落级指标归因的最小段长——与 computeBaseline 的「<40 字章节不参与基线」口径一致。 */
77
86
  const MIN_PARA_CHARS = 40;
@@ -152,6 +161,21 @@ const METRIC_DIRECTION = {
152
161
  }
153
162
  };
154
163
 
164
+ /**
165
+ * v5.0.0 修正(降本):指标类 hint 的**软化后缀**——只加在「抒情/心理/留白段落本来就这么写」的三个维度上。
166
+ * 抽象度过高不该一律改:抒情与心理段落本就偏抽象;留白异常(省略号/破折号)常是刻意的节奏手段;
167
+ * hedgeDensity(似乎/仿佛/大概)更是限知视角下的正常写法。这三类若一律按「待改」处理,
168
+ * 模型会把全章语气统一成同一种(实测反馈:越改越僵),故只提示「读起来确实别扭才改」并给出 skip 出口。
169
+ * 其余维度(complexity / modifierDensity / actionDensity)是明确的句式问题,不加此句,保持指令的直接性。
170
+ */
171
+ const METRIC_HINT_SOFT = { abstractDensity: true, gapIndex: true, hedgeDensity: true };
172
+ const METRIC_HINT_SOFT_SUFFIX = "(仅当这里读起来确实别扭才改;抒情、心理、留白段落属正常写法,可标记 skip 保留。)";
173
+
174
+ /** v5.0.0 修正(降本):取该维度的软化后缀(不属于上述三类则返回空串,hint 文案保持原样)。 */
175
+ function softSuffix(metric) {
176
+ return METRIC_HINT_SOFT[metric] ? METRIC_HINT_SOFT_SUFFIX : "";
177
+ }
178
+
155
179
  /**
156
180
  * v5.0.0:「情感过直」判定词表——程度副词 + 情绪词紧邻出现(中间只允许 0-2 字)。
157
181
  * 刻意只在「全章命中 ≥ 3 次」时才报:单次出现多半是正常行文(「她非常高兴地答应了」)。
@@ -383,7 +407,8 @@ function collectMetricItems(judge, baseline, tol, index, anchors, notes) {
383
407
  anchor,
384
408
  sev,
385
409
  2, // v5.0.0:段落级问题只需改这一段 → 最容易改
386
- METRIC_LABELS[v.metric] + "偏离:全章 " + v.value + "(基线 " + v.mu + ",偏差 " + devText + "),本段 " + h.value + "。" + guide + "。只改这一段的写法,不要整章重写。"
410
+ METRIC_LABELS[v.metric] + "偏离:全章 " + v.value + "(基线 " + v.mu + ",偏差 " + devText + "),本段 " + h.value + "。" + guide + "。只改这一段的写法,不要整章重写。" +
411
+ softSuffix(v.metric)
387
412
  ));
388
413
  }
389
414
  } else {
@@ -397,7 +422,8 @@ function collectMetricItems(judge, baseline, tol, index, anchors, notes) {
397
422
  Math.max(1, sev - 1), // v5.0.0:无段落归因 → 无具体落点,严重度降一档
398
423
  3,
399
424
  METRIC_LABELS[v.metric] + "偏离:全章 " + v.value + "(基线 " + v.mu + ",偏差 " + devText + ")。" + guide + "。" +
400
- "该维度没有单段显著偏离,属**全章层面**的问题(本章各段写法普遍如此)——请按上面方向通读本章相关段落处理,不要只改一句。"
425
+ "该维度没有单段显著偏离,属**全章层面**的问题(本章各段写法普遍如此)——请按上面方向通读本章相关段落处理,不要只改一句。" +
426
+ softSuffix(v.metric)
401
427
  ));
402
428
  }
403
429
  }
@@ -426,7 +452,8 @@ function collectMetricItems(judge, baseline, tol, index, anchors, notes) {
426
452
  4, // 无 σ 可比,取固定严重度:全书从未出现过的写法突然大量出现,属明确异常
427
453
  3,
428
454
  METRIC_LABELS[sk.metric] + "异常:全书基线该维度恒为 0(从未出现过),本章却达到 " + value + "(量级门槛 " + minDelta + ")。" + guide + "。" +
429
- "该维度没有基线段落分布可比,属**全章层面**的问题——请通读本章相关段落处理,不要只改一句。"
455
+ "该维度没有基线段落分布可比,属**全章层面**的问题——请通读本章相关段落处理,不要只改一句。" +
456
+ softSuffix(sk.metric)
430
457
  ));
431
458
  }
432
459
  if (skipped.length > 0 && notes) {
package/lib/index.js CHANGED
@@ -651,6 +651,9 @@ function registerNovelSentenceAnalysis(ctx, config) {
651
651
  // v3.9.5 修正:缓存键纳入 top/maxSentences/curveSegments 与 enabled/autoAnalyze——参数不同不再命中旧结果
652
652
  const cacheKey = createHash("sha1").update(book + "|" + scope + "|" + scopeKeyParts.join(",") + "|top=" + top + "|max=" + maxSentences + "|curve=" + curveSegments + "|en=" + (effective.enabled ? 1 : 0) + "|auto=" + (effective.autoAnalyze ? 1 : 0)).digest("hex").slice(0, 20);
653
653
  const reportFile = join(cacheDir, book + "-" + cacheKey + ".json");
654
+ // v5.0.0(降本):精简工作流开启且调用方未显式指定 brief 时,句式分析默认走简报(完整版约 2k 字符)。
655
+ // 命中与未命中两条路径共用同一个判据,避免口径漂移。
656
+ const sentenceBrief = args?.brief === true || (args?.brief === void 0 && isLeanWorkflow());
654
657
  let cachedHit = null;
655
658
  if (!fresh) {
656
659
  try {
@@ -683,7 +686,7 @@ function registerNovelSentenceAnalysis(ctx, config) {
683
686
  }
684
687
  // v4.3.0:emotionCaveat 与 emotionComplexity 各自独立生效(旧版 cropEmotion 连带剥掉 quantification)
685
688
  cachedHit.emotion = cropEmotionByFeatures(state, cachedHit.emotion);
686
- if (args?.brief === true && cachedHit.categories && cachedHit.lengths && cachedHit.emotion) {
689
+ if (sentenceBrief && cachedHit.categories && cachedHit.lengths && cachedHit.emotion) {
687
690
  cachedHit.brief = buildBriefLine(cachedHit, scope);
688
691
  }
689
692
  // v3.5.0 H2:缓存命中剥离内部 ver 键(契约:output schema 无 ver)
@@ -745,7 +748,7 @@ function registerNovelSentenceAnalysis(ctx, config) {
745
748
  } catch { /* 缓存写失败不影响结果 */ }
746
749
  // v4.3.0:两个情感开关独立生效(旧版 cropEmotion 连带剥掉 quantification,complexity 分支成死代码)
747
750
  result.emotion = cropEmotionByFeatures(state, result.emotion);
748
- if (args?.brief === true && result.categories && result.lengths && result.emotion) {
751
+ if (sentenceBrief && result.categories && result.lengths && result.emotion) {
749
752
  result.brief = buildBriefLine(result, scope);
750
753
  }
751
754
  // v4.0.0 修正:本工具未过 cleanOutput,stats 里的 -0 会被宿主 lossless JSON 校验整次拒绝
@@ -756,7 +759,7 @@ function registerNovelSentenceAnalysis(ctx, config) {
756
759
  function registerNovelSentenceConfig(ctx, config) {
757
760
  ctx.tools.register({
758
761
  name: "novel_sentence_config",
759
- description: "查看或修改句式模式分析的开关状态:enabled(分析功能是否可用)、autoAnalyze(分析作品时是否主动使用)、systemPromptMode(系统提示词档位 off/brief/full)与 promptScene(提示词场景 general/writing/revising/auditing/setup,决定完整档下注入哪一套流程)。",
762
+ description: "查看或修改句式模式分析的开关状态:enabled(分析功能是否可用)、autoAnalyze(分析作品时是否主动使用)、systemPromptMode(系统提示词档位 off/brief/full)、promptScene(提示词场景 general/writing/revising/auditing/setup,决定完整档下注入哪一套流程)与 leanWorkflow(精简工作流:打开后只按需调用工具,省 token)。",
760
763
  parameters: {
761
764
  type: "object",
762
765
  properties: {
@@ -766,6 +769,8 @@ function registerNovelSentenceConfig(ctx, config) {
766
769
  // v5.0.0:提示词"档位 × 场景"两个正交维度。档位决定注入多少,场景决定注入什么。
767
770
  systemPromptMode: { type: "string", enum: ["off", "brief", "full"], description: "系统提示词档位:off=不注入 / brief=精简(只说明有这套工具) / full=完整(按场景注入对应流程)。" },
768
771
  promptScene: { type: "string", enum: [...PROMPT_SCENES], description: "提示词场景(仅 full 档生效):general=通用写作工作流(默认,等价于 4.x 行为) / writing=写新章 / revising=改稿 / auditing=审计(只体检不改稿) / setup=建资料(只维护设定与创作资料)。" },
772
+ // v5.1.0(降本):精简工作流——打开后注入最简档,去掉自检/复测/登记的全部催促
773
+ leanWorkflow: { type: "boolean", description: "精简工作流(默认 false):true=注入最简提示词(优先于档位与场景),只保留「工具按需调用」一条原则,报告类工具未显式传 brief 时默认走精简输出。适合成本敏感的长篇。" },
769
774
  tools: { type: "object", additionalProperties: true, description: "各工具开关(如 { novel_plot: false }),键必须是 novel_* 工具名。" },
770
775
  features: { type: "object", additionalProperties: true, description: "功能开关(emotionCaveat=情感净化预警 / genreTheme=题材与流派检测 / webnovelVibe=网文信号 / rawWriting=非净化直白模式 / semanticEmbedding=本地语义增强),如 { emotionCaveat: false }。注意:rawWriting=true 会跳过 UI 的双重确认+承诺输入流程,仅应在用户明确要求时开启。" },
771
776
  styleTolerance: { type: "object", additionalProperties: true, description: "风格基线容差(每维 { low: -20, high: 20 },low 为负/高为正;空对象 {} 清除恢复推荐——宿主不支持 null 类型,清除一律用空对象)。" },
@@ -785,6 +790,8 @@ function registerNovelSentenceConfig(ctx, config) {
785
790
  // v5.0.0:提示词档位与场景(此前这对开关只在侧边栏可见,模型侧看不到自己的注入状态)
786
791
  systemPromptMode: { type: "string" },
787
792
  promptScene: { type: "string" },
793
+ // v5.1.0(降本):精简工作流(与 promptScene 同级;仅接受布尔值)
794
+ leanWorkflow: { type: "boolean" },
788
795
  source: { type: "string" },
789
796
  updated: { type: "boolean" },
790
797
  tools: { type: "object", additionalProperties: true, description: "各工具当前开关状态。" },
@@ -807,6 +814,7 @@ function registerNovelSentenceConfig(ctx, config) {
807
814
  写作助手功能: ${value.enabled ? "已启用" : "已关闭"}
808
815
  自动分析(autoAnalyze): ${value.autoAnalyze ? "开" : "关"}
809
816
  系统提示词: 档位=${value.systemPromptMode ?? "brief"} 场景=${value.promptScene ?? "general"}${(value.systemPromptMode ?? "brief") === "full" && (value.promptScene ?? "general") !== "general" ? "(按场景注入)" : ""}
817
+ 精简工作流(leanWorkflow): ${value.leanWorkflow === true ? "开(优先于档位/场景,只按需调用工具)" : "关"}
810
818
  来源: ${value.source}
811
819
  ${value.updated === true ? "(已保存到 state 文件)" : ""}
812
820
 
@@ -833,6 +841,8 @@ ${featLines.join("\n")}
833
841
  // v5.0.0:提示词档位与场景(非法值直接忽略;general 是显式合法值,用于改回通用档)
834
842
  if (["off", "brief", "full"].includes(args?.systemPromptMode)) patch.systemPromptMode = args.systemPromptMode;
835
843
  if (PROMPT_SCENES.includes(args?.promptScene)) patch.promptScene = args.promptScene;
844
+ // v5.1.0(降本):精简工作流(只接受布尔值;非布尔静默忽略,与 core.writeSentenceState 同口径)
845
+ if (typeof args?.leanWorkflow === "boolean") patch.leanWorkflow = args.leanWorkflow;
836
846
  if (args?.tools !== null && typeof args?.tools === "object") {
837
847
  const toolsPatch = {};
838
848
  for (const name of ALL_TOOLS) {
@@ -906,7 +916,7 @@ ${featLines.join("\n")}
906
916
  try { await syncCreationProfileFiles(syncRoot, args.creationProfiles, current.creationProfiles); } catch { /* 文件同步失败不影响设定保存 */ }
907
917
  }
908
918
  }
909
- return { file: stateFilePath(), enabled: after.enabled, autoAnalyze: after.autoAnalyze, source, updated, systemPromptMode: current?.systemPromptMode ?? "brief", promptScene: current?.promptScene ?? "general", tools, features, styleTolerance, creationProfile, creationProfiles, embedding: { available: embAvailable, loaded: embStatus.loaded, error: embError } };
919
+ return { file: stateFilePath(), enabled: after.enabled, autoAnalyze: after.autoAnalyze, source, updated, systemPromptMode: current?.systemPromptMode ?? "brief", promptScene: current?.promptScene ?? "general", leanWorkflow: current?.leanWorkflow === true, tools, features, styleTolerance, creationProfile, creationProfiles, embedding: { available: embAvailable, loaded: embStatus.loaded, error: embError } };
910
920
  }
911
921
  });
912
922
  }
@@ -972,7 +982,9 @@ function registerNovelStyleCheck(ctx, config) {
972
982
  }
973
983
  if (value.semantic && value.semantic.note) lines.push(`语义对比: ${value.semantic.note}` + (typeof value.semantic.similarity === "number" ? `(${(value.semantic.similarity * 100).toFixed(1)}%)` : ""));
974
984
  if (Array.isArray(value.fixAnchors) && value.fixAnchors.length > 0) {
975
- lines.push("", "【对照修正·原著锚段】逐句对比本章对应写法,只修跑偏部分,不得整章重写:");
985
+ // v5.1.0(降本):抬头从"逐句对比…只修跑偏部分"改为条件式——只有确实跑偏时才用锚段对照,
986
+ // 且明确"改那几处",避免模型把正常波动也当成必须修的偏差。
987
+ lines.push("", "【对照参考·原著锚段】若确实跑偏(偏离明显或读起来别扭),可对照这几段原著的写法只调整那几处,不要整章重写:");
976
988
  for (const a of value.fixAnchors) lines.push(" [" + a.label + "] " + a.text);
977
989
  }
978
990
  lines.push(`本章指纹: ${value.chapterFingerprint}`);
@@ -988,7 +1000,9 @@ function registerNovelStyleCheck(ctx, config) {
988
1000
  : (v.devPct > 0 ? "+" : "") + v.devPct + "%";
989
1001
  lines.push(` ${v.label}: 本章 ${v.value} vs 基线 ${v.mu}(${devText},${mark})`);
990
1002
  }
991
- if (value.metric.outCount > 0) lines.push(" ⚠ " + value.metric.summary + "——请对照基线修正后再续写");
1003
+ // v5.1.0(防僵化):删掉句尾「——请对照基线修正后再续写」——它与同一份输出里的建议段自相矛盾
1004
+ // (建议段说"轻微出带属正常波动,不必逐项消除");模型会听更早、更硬的那半句,于是反复打磨。
1005
+ if (value.metric.outCount > 0) lines.push(" ⚠ " + value.metric.summary);
992
1006
  // v4.3.0:即使没有出带维度,也要披露"因基线为 0 未做相对判定"的维度(旧版一律不打印)
993
1007
  else if ((value.metric.skippedDims || []).length > 0) lines.push(" · " + value.metric.summary);
994
1008
  } else if (value.metric && !value.metric.enabled) {
@@ -1055,7 +1069,7 @@ function registerNovelStyleCheck(ctx, config) {
1055
1069
  // v3.7.0 ⑦:方向全映射(原只认"偏高","偏长/偏低/偏短"一律输出"略少")
1056
1070
  const noteMap = { "偏高": "略多", "偏低": "略少", "偏长": "略长", "偏短": "略短" };
1057
1071
  // v3.8.0:跑偏不整章重写——对照 fixAnchors(原著锚段)逐句修正跑偏维度
1058
- advice = "续写风格偏差:" + diffs.slice(0, 4).map((d) => d.dimension + (noteMap[d.note] || d.note)).join("、") + "。请对照 fixAnchors 中的原著锚段,逐句对比本章对应写法,只修改跑偏的部分(句式/修饰/节奏),不要整章重写;若为情节需要(如章节情绪转折),说明原因后可接受,但不要持续漂移。";
1072
+ advice = "续写风格偏差:" + diffs.slice(0, 4).map((d) => d.dimension + (noteMap[d.note] || d.note)).join("、") + "。**先判断这是不是有意为之**:章节类型不同(抒情段 / 动作段 / 对话段)本来就会偏离全书均值——确属风格选择就保留;确实跑偏时,可对照 fixAnchors 中的原著锚段只调整那几处(句式/修饰/节奏),不要整章重写,也不要为对齐数字改文。";
1059
1073
  }
1060
1074
  // v2.0.0:语义级风格对比(本地 embedding,可选增强;开关开且模型可用时生效)
1061
1075
  let semantic = null;
@@ -1146,8 +1160,8 @@ function registerNovelStyleCheck(ctx, config) {
1146
1160
  const outDims = (metric.verdicts || []).filter((v) => v.status === "out").map((v) => v.label).slice(0, 4).join("/");
1147
1161
  const dimNote = metric.summary + (outDims ? "(" + outDims + ")" : "");
1148
1162
  advice = diffs.length === 0
1149
- ? "句式指纹与全书高度一致,但" + dimNote + "——请对照 fixAnchors 中的原著锚段修正出带维度后再续写。"
1150
- : advice + "另:" + dimNote + "。";
1163
+ ? "句式指纹与全书高度一致;六维层面:" + dimNote + "——**单一维度出带属正常波动**(带宽 = 作者自身波动的 1.5σ),只有偏离达到 2 倍容差以上、或那一处读起来确实别扭时才需要调整,不要为对齐数字改写文本。"
1164
+ : advice + "另(六维):" + dimNote + "——同上,轻微出带属正常波动,不必逐项消除。";
1151
1165
  }
1152
1166
  return cleanOutput({
1153
1167
  book,
@@ -1357,6 +1371,9 @@ function registerNovelFixPlan(ctx, config) {
1357
1371
  const out = [`<path>novels/${value.book}</path>`, "<type>novel-fix-plan</type>", "<content>", "", value.summary];
1358
1372
  if (Array.isArray(value.items) && value.items.length > 0) {
1359
1373
  out.push("", `待办 ${value.items.length} 条(按 严重度 → 难度 → 行号 排序):`);
1374
+ // v5.0.0(降本):明示"分批 + 单次复测"——旧版没有这句,模型会逐条改 + 逐条 mark + 反复 verify,
1375
+ // 实测一次改稿 30+ 次调用(每次重发整章),这也是"改稿比单写还贵"的直接原因。
1376
+ out.push(" 处理建议:**分批改**(同一段落/同一类型的多条合并成一次修改),一轮处理最严重的 3~5 条即可;不必逐条 mark,改完最多 verify 一次。指标类(抽象度/留白/不确定性/情感过直)先判断再动手:抒情、心理、留白段属正常写法,读起来不别扭就保留。");
1360
1377
  for (const it of value.items) {
1361
1378
  const loc = it.locate.lineStart > 0 ? `第 ${it.locate.lineStart}${it.locate.lineEnd !== it.locate.lineStart ? "-" + it.locate.lineEnd : ""} 行` : "全章层面";
1362
1379
  // v5.0.0 修正:把 itemId 渲染出来。此前 mark 需要的 id 只存在于返回体/落盘文件里,
@@ -1671,7 +1688,8 @@ function registerNovelStyleReport(ctx, config) {
1671
1688
  ? "\n✅ 风格判断已保存:" + (saved.savedAt || "(存盘失败)")
1672
1689
  : "\n📌 请结合上述六个维度数据判断本书的风格气质、读者感受与写法特征,";
1673
1690
  // v3.9.1 修复:brief=true 时真正精简输出(此前仅附加 brief 字段、report 仍全文+锚包,未达省 token 目的)
1674
- const briefMode = args?.brief === true;
1691
+ // v5.0.0(降本):精简工作流开启且调用方没显式指定 brief 时,默认走精简输出(省掉 2k+ 字符的报告全文)
1692
+ const briefMode = args?.brief === true || (args?.brief === void 0 && isLeanWorkflow());
1675
1693
  // v4.0.0 修正:空基线(无有效章节)只有 mu:0 且无 recTol → 旧版输出"推荐容差 undefined%"并谎报 μ=0
1676
1694
  if (briefMode && result.baseline && result.baselineChapterCount > 0) {
1677
1695
  // v3.9.1 优化:按 METRIC_ORDER 顺序输出、用中文标签(与完整报告一致)
@@ -3337,6 +3355,13 @@ function safeTitleSegment(title) {
3337
3355
  return "";
3338
3356
  }
3339
3357
  }
3358
+ /**
3359
+ * v5.0.0(降本):精简工作流开关。true 时注入 LEAN_TEXT、报告类工具默认走精简输出。
3360
+ * 状态字段由 core.js 持久化(缺省/非法值一律 false),这里只做只读判断。
3361
+ */
3362
+ function isLeanWorkflow() {
3363
+ try { return readSentenceStateSync()?.leanWorkflow === true; } catch { return false; }
3364
+ }
3340
3365
  function registerNovelNewChapter(ctx, config) {
3341
3366
  ctx.tools.register({
3342
3367
  name: "novel_new_chapter",
@@ -3459,7 +3484,7 @@ function registerNovelNewChapter(ctx, config) {
3459
3484
  try {
3460
3485
  anchorPkg = await buildStyleAnchorPackage(root, book, chapters, exec, anchorTexts); // 复用基线计算已读文本
3461
3486
  } catch { /* 锚包失败不影响创建 */ }
3462
- const reminder = "本章已创建(第" + String(number).padStart(2, "0") + "章)。【风格基线强制流程】① 动笔前:对照上方 baseline 六维 μ,并按 anchors/skeletons 原著样例的味道与句式骨架写(数字只做校验,样例才是写法);② 写完:必须调用 novel_style_check 对照——verdict 低于 high 或出带时,按返回的 fixAnchors 原著锚段逐句修正跑偏部分,不得整章重写;属情节需要则说明原因。";
3487
+ const reminder = "本章已创建(第" + String(number).padStart(2, "0") + "章)。【基线怎么用】① 动笔前:anchors 读一遍校准语感即可(**不要照 skeletons 造句**),baseline 六维只做写完后的校验;② 写完:**建议**跑一次 novel_style_check,只有偏离明显(≥2 倍容差)或那一处读起来确实别扭时才逐句修正跑偏处(不得整章重写)——单一维度轻微出带属正常波动,不要为对齐数字改文。";
3463
3488
  await noteRoot(root); // v3.9.0:新章创建记录书库根
3464
3489
  return cleanOutput({ book, number, file: fileName, path: filePath, chars: text.length, baseline: baselineLine, reminder, anchors: anchorPkg.anchors, skeletons: anchorPkg.skeletons });
3465
3490
  }
@@ -3509,7 +3534,8 @@ function apply(ctx, config = {}) {
3509
3534
  const st = readSentenceStateSync();
3510
3535
  // v5.0.0:档位(注入多少)× 场景(注入什么)。选择逻辑收进 prompts.js 的纯函数 promptTextFor——
3511
3536
  // 这里只负责读状态与兜底,便于单测覆盖"老状态文件(无 promptScene)行为不变"。
3512
- return promptTextFor(st.systemPromptMode, st.promptScene);
3537
+ // v5.0.0(降本):leanWorkflow=true 时优先注入精简档(LEAN_TEXT),优先于场景——省 token 比"注入什么"更要紧
3538
+ return promptTextFor(st.systemPromptMode, st.promptScene, st.leanWorkflow === true);
3513
3539
  } catch { /* 读不到开关时按精简档处理 */ }
3514
3540
  return BRIEF_TEXT;
3515
3541
  }
package/lib/prompts.js CHANGED
@@ -29,7 +29,7 @@ const WORKFLOW_TEXT = [
29
29
  " 【v5.0.0·开写包】动笔前优先调用 novel_chapter_brief(chapter=\"next\")——**一次**拿到:上一章结尾原文(承接口)、本章大纲方向、相关人物卡、",
30
30
  " 待回收伏笔(含埋设距离)、世界观用语规范、六维风格基线、原著锚段与句式骨架、本章禁用清单与开写清单。比逐个工具去翻更省上下文,而且不会漏读——",
31
31
  " 漏读正是文风漂移与设定矛盾的头号来源。返回体里的 degraded 列出「哪些材料没取到及原因」,缺什么就在计划里说明,不要凭空补。",
32
- " 【强制·风格基线】动笔前必须调用 novel_style_report 读取全书六维风格基线(μ/σ/容差带)并记录;novel_new_chapter 创建章节时会自动附基线 μ 摘要——若基线与你从正文感受的文风不符,以基线数据为准。",
32
+ " 【风格基线·按需取用】基线不用专门去调工具:novel_chapter_brief 与 novel_new_chapter 会直接附上(μ/σ/容差带)。数字只做**事后**校验,不要翻译成写作规则;与你的语感冲突时以语感为准,数字只用来看「漂到哪一维」。",
33
33
  " 【v3.8.0·锚包写作】novel_style_report / novel_new_chapter 返回的 anchors(原著代表性段落)与 skeletons(句式骨架)是风格锚——动笔时照锚段的味道与句式的形状写,数字基线只做事后校验;不要试图把数字翻译成写作规则。",
34
34
  " 新章节写入 novels/<书名>/ 文件夹:用 novel_new_chapter 创建章节文件,或直接用 write 工具写文件。",
35
35
  "4. 句式模式分析(增强,受 UI 开关控制):分析作品时可调用 novel_sentence_analysis 提取句式分布(陈述/环境/心理/对话/疑问/反问/感叹/祈使/省略留白九类)、",
@@ -47,7 +47,7 @@ const WORKFLOW_TEXT = [
47
47
  " 改完用 action=\"verify\" 复测三态(resolved/pending/new),单项处置完可用 action=\"mark\"(itemId + state=done/skip)登记。它只给方向、不生成正文。",
48
48
  " 【v5.0.0·结构视图】查跨章结构问题用 novel_plot action=\"graph\":伏笔埋设跨度、人物连续缺席、剧情线空档、时间线顺序、大纲方向与正文的偏离。",
49
49
  " 它回答的是长篇最容易崩的那三件事——线断了、人丢了、伏笔忘了;novel_plot 的登记类 action 仍是同表维护。",
50
- " 【强制·写完对照】每写完一章必须调用 novel_style_check 对照基线(相似度+偏差清单+六维判定)——verdict 低于 high 或任一维度出带时,按返回的 fixAnchors(原著锚段)逐句对照修正跑偏部分,不得整章重写;确属情节需要则说明原因;novel_plot scan 自动更新伏笔提及记录;",
50
+ " 【写完对照·按需】写完一章**建议**跑一次 novel_style_check;只有相似度明显偏低、或某一维偏离超过 2 倍容差/越过量级门槛时,才按返回的 fixAnchors(原著锚段)逐句对照修正跑偏处(不得整章重写)。**单一维度轻微出带属正常波动**(1.5σ 带宽下六维里至少一维出带是常见现象),不要为对齐数字改文;确属情节或文体需要,说明理由即可;novel_plot scan 只在用户要求时跑;",
51
51
  " 世界观一致性:续写前先确认文化基准——novel_settings category=worldview action=detect 自动判断(或人工 add/update),动笔时对照其 bannedWords/recommended 用词,避免中西意象混搭(如欧式背景不写老夫/上香/时辰);novel_continuity_check 会扫描用语冲突候选;",
52
52
  " 语用一致性:不只管词,还要管'怎么说话'——对照 worldview 的 speechStyle(title 称谓规范/honorBad 客套禁词/ritualBadPatterns 仪式禁式/tone 语气),人物开口前检查称谓是否欧式(Miss+名)、客套是否避免'提点/承蒙/在下'、宗教仪式是否点烛而非烧香/上X柱香、对话是否口语化不文言;",
53
53
  " novel_settings 维护五张设定表(人物/地点/道具/时间线/世界观用语规范),novel_summary 保存每章摘要(长书续写先读摘要再按需细读),novel_continuity_check 输出设定矛盾候选;",
@@ -72,32 +72,41 @@ const BRIEF_TEXT = [
72
72
  * 因此**既有工作流不变**——本版只是在其中补入三条新工具的指引(+7 行),其余逐字沿用 4.x。
73
73
  */
74
74
 
75
- /** 场景 writing:写新章(材料装配 → 动笔 → 事后对照 → 收尾登记)。 */
75
+ /** 场景 writing:写新章(材料装配 → 动笔 → 按需自检 → 按需收尾)。
76
+ * v5.0.0 修正(降本):原版把「强制自检」「收尾三件」写成编号必做项,实测每章固定多出 3 次工具调用,
77
+ * 而每次调用都要重发整章正文(成本主因之一);同时"照骨架的形状写"会让句式趋同。这里全部降级为按需。 */
76
78
  const SCENE_WRITING = [
77
79
  "【小说写作助手 · 写新章】",
78
- "1. 先取材料,不要自己逐个工具翻:调用 novel_chapter_brief(chapter=\"next\")一次拿到——上一章结尾原文(承接口)、本章大纲方向、",
79
- " 相关人物卡、待回收伏笔(含「埋了多久没回收」)、世界观用语规范、六维风格基线、原著锚段与句式骨架、以及本章禁用清单。",
80
- " 返回体里的 degraded 字段列出了「哪些材料没取到及原因」,缺什么就在计划里说明,不要凭空补。",
81
- "2. 动笔前用两三句简述:上一章结尾状态 → 本章目标 → 写作计划(计划要说明如何贴合基线六维)。",
82
- "3. 正文照 anchors(原著锚段)的味道与 skeletons(句式骨架)的形状写;数字基线只做事后校验,不要把数字翻译成写作规则。",
80
+ "1. 先取材料,不要自己逐个工具翻:调用 novel_chapter_brief(chapter=\"next\",默认 budget=\"compact\")一次拿到——上一章结尾原文(承接口)、",
81
+ " 本章大纲方向、相关人物卡、待回收伏笔(含「埋了多久没回收」)、世界观用语规范、六维风格基线、原著锚段与句式骨架、以及本章禁用清单。",
82
+ " 返回体里的 degraded 列出了「哪些材料没取到及原因」,缺什么就在计划里说明,不要凭空补。**不要为了拿材料再逐个调用别的工具**。",
83
+ "2. 动笔前用两三句简述:上一章结尾状态 → 本章目标 → 写作计划,再给正文。",
84
+ "3. anchors(原著锚段)**只用来校准语感**:读一遍,记住「读起来是什么味道」就够了。**不要照 skeletons(句式骨架)造句、不要套用它们的句式模板**——",
85
+ " 骨架只在你写不出该类型句子时参考。目标是「像这位作者写的」,不是「像这位作者的句子拼出来的」。",
83
86
  "4. avoid(本章禁用清单)里的东西不要写:已回收的伏笔不复埋、已退场的人物不凭空出现、worldview.bannedWords 里的词不用。",
84
- "5. 用 novel_new_chapter 建章节文件(或 write 直接写),然后【强制】调用 novel_style_check 对照基线:",
85
- " verdict 低于 high 或任一维度出带时,按返回的 fixAnchors 逐句对照修正,**不得整章重写**;确属情节需要则说明理由。",
86
- "6. 收尾三件:novel_plot add 登记本章新埋的伏笔、novel_outline hook 回填本章结尾钩子、novel_summary 存本章摘要。",
87
- "7. 句式模式是「参考节奏」而非「模板套用」——机械复刻导致句子僵硬时,优先回归自然表达。"
87
+ "5. 用 novel_new_chapter 建章节文件(或 write 直接写)。写完**建议**跑一次 novel_style_check;只有相似度明显偏低、或某一维偏离超过 2 倍容差时,",
88
+ " 才逐句修正跑偏处(不得整章重写)——**单一维度轻微出带属正常波动,不要为对齐数字改文**。",
89
+ "6. 收尾**按需**,不必每章全套:本章确实新埋了伏笔才 novel_plot add;钩子与摘要等章节稳定后(或用户要求时)再回填 novel_outline hook / novel_summary。",
90
+ "7. 最重要的一条:**读起来自然 > 指标好看**。句式模式与六维基线都是参考节奏,机械复刻导致句子僵硬、重复、模式化时,一律优先回归自然表达。"
88
91
  ].join("\n");
89
92
 
90
93
  /** 场景 revising:改稿(清单驱动 → 逐条改 → 复测)。 */
94
+ /** 场景 revising:改稿(清单驱动 → **分批**改 → 单次复测)。
95
+ * v5.0.0 修正(降本):原版要求「逐条处理 + 每条 mark + 每批 verify + 全部处理完再 verify」,
96
+ * 实测一次改稿 30+ 次调用(每次都要重发整章正文),这正是「改稿比单写还贵」的直接原因;
97
+ * 同时「指标类一律照改」会把抒情/留白段落磨平成同一语气(僵化)。现在改为:分批合并、只处理最严重的几条、mark 可选、verify 只跑一次。 */
91
98
  const SCENE_REVISING = [
92
99
  "【小说写作助手 · 改稿】",
93
100
  "1. 先拿清单,不要凭感觉改:调用 novel_fix_plan(action=\"plan\")得到按优先级排好的待办——每条含原句位置(章节 + 行号 + 原句)、",
94
- " 当前值与目标值、可参照的原著锚段、以及改写方向 hint。",
95
- "2. 按 severity 从高到低逐条处理;**只改有问题的那几句/那一段**,其余部分原样保留,不要整章重写。",
96
- "3. hint 只给方向、不给句子(本插件不生成正文):具体怎么改由你决定,但必须保持与原文一致的用词习惯与叙事风格。",
97
- "4. 每改完一批就登记并复测:novel_fix_plan(action=\"mark\", itemId=..., state=\"done\")→ action=\"verify\" 看三态",
98
- " (resolved 已回到带内 / pending 仍偏离 / new 新增偏离),只继续处理 pending 与 new。",
99
- "5. 全部处理完再跑一次 novel_style_check 确认整体没有跑偏;若相似度反而下降,回退该处修改。",
100
- "6. 不要为了让数字好看而牺牲自然表达。确属情节需要的偏离,说明理由即可——基线是参考,不是镣铐。"
101
+ " 当前值与目标值、可参照的原著锚段、以及改写方向 hint。清单是「先改最要紧的」,**不是「一次改完」**。",
102
+ "2. **分批改,不要一条一次编辑**:从 severity 最高的开始,**同一段落、同一类型的多条合并成一次修改**;一轮只处理最严重的 3~5 条",
103
+ " (或 severity ≥ 4 的项)。剩下的条目留在清单里,用户需要时再继续——不要为了清零而反复往返。",
104
+ "3. **只改有问题的那几句/那一段**,其余原文原样保留,不要整章重写。hint 只给方向不给句子:具体怎么写由你决定,但必须保持与原文一致的用词习惯与叙事风格。",
105
+ "4. **不要逐条 mark,也不要反复 verify**:用户没有要求跟踪处置状态时跳过 mark;一轮改完**只跑一次** novel_fix_plan(action=\"verify\")看三态,",
106
+ " 用来知道「还剩什么」,而不是必须消灭的待办——pending 里有轻微项是正常的。",
107
+ "5. 指标类待办(抽象度过高 / 留白异常 / 不确定性 / 情感过直)**要判断再动手**:抒情、心理、留白、节奏变化本来就是正常写法,",
108
+ " 只有当那一处**读起来确实别扭**时才改;不确定就保留,或用 mark 标 skip 记一笔。",
109
+ "6. 最重要的一条:**基线是参考,不是镣铐**。不要为了让数字好看而牺牲自然表达;确属情节或文体需要的偏离,说明理由即可。"
101
110
  ].join("\n");
102
111
 
103
112
  /** 场景 auditing:审计(只体检、只报告,不改稿)。 */
@@ -134,6 +143,22 @@ const SCENE_TEXTS = Object.freeze({
134
143
  setup: SCENE_SETUP
135
144
  });
136
145
 
146
+ /**
147
+ * v5.0.0(降本):精简工作流档(`leanWorkflow=true` 时注入,**优先级高于场景**)。
148
+ *
149
+ * 背景:真实用户反馈成本从 0.2~0.3 元/万字涨到 0.7 元——主因之一是提示词里的强制步骤链
150
+ * (写完必自检 + 收尾三件 + 改稿逐条 mark/verify)让每章多出数次工具调用,而每次调用都要重发整章正文。
151
+ * 打开本开关后只保留「按需调用」这一条原则,把动作全部交回给用户与模型判断。
152
+ */
153
+ const LEAN_TEXT = [
154
+ "【小说写作助手 · 精简工作流】",
155
+ "0. 本档只保留一条原则:**工具按需调用**。用户没要求时,不要主动跑风格自检、不要复测改稿清单、不要登记伏笔/回填钩子/存摘要。",
156
+ "1. 需要动笔材料时:novel_chapter_brief(budget=\"compact\")一次取齐,不要为同一件事再逐个调用别的工具。",
157
+ "2. 需要改稿意见时:novel_fix_plan(action=\"plan\")拿清单;**分批改**(同段同类合并成一次),改完最多跑一次 verify,不要逐条 mark、不要反复复测。",
158
+ "3. 六维基线与原著锚段只做**事后校验**与语感参照:锚段读一遍记味道即可,**不要照 skeletons 造句**;不要为对齐数字改写文本,读起来自然优先。",
159
+ "4. 用户明确要求的分析、报告、登记照常执行;没要求的长流程(通读全书、逐章对照)先问一句再做。"
160
+ ].join("\n");
161
+
137
162
  /** 合法场景枚举(`general` 必须在内,它是向后兼容的默认值)。 */
138
163
  const PROMPT_SCENES = Object.freeze(["general", "writing", "revising", "auditing", "setup"]);
139
164
 
@@ -142,16 +167,18 @@ const PROMPT_SCENES = Object.freeze(["general", "writing", "revising", "auditing
142
167
  *
143
168
  * - mode 为 off → 返回空串(DSH 会丢弃该段)
144
169
  * - mode 为 brief → BRIEF_TEXT(精简档与场景无关,它只说明"有这套工具")
145
- * - mode 为 full + 未知/缺省场景 → WORKFLOW_TEXT(4.x 原文 + 本版补入的 7 行新工具指引)
170
+ * - mode 为 full + lean → LEAN_TEXT(v5.0.0 降本档,优先于场景:省 token 比"注入什么"更重要)
171
+ * - mode 为 full + 未知/缺省场景 → WORKFLOW_TEXT(4.x 原文 + 本版补入的新工具指引)
146
172
  * - mode 为 full + 已知场景 → 该场景文本
147
173
  */
148
- function promptTextFor(mode, scene) {
174
+ function promptTextFor(mode, scene, lean) {
149
175
  if (mode === "off") return "";
150
176
  if (mode === "brief") return BRIEF_TEXT;
177
+ if (lean === true) return LEAN_TEXT;
151
178
  if (typeof scene === "string" && Object.prototype.hasOwnProperty.call(SCENE_TEXTS, scene)) {
152
179
  return SCENE_TEXTS[scene];
153
180
  }
154
181
  return WORKFLOW_TEXT;
155
182
  }
156
183
 
157
- export { RAW_WRITING_PROMPT, WORKFLOW_TEXT, BRIEF_TEXT, SCENE_TEXTS, PROMPT_SCENES, promptTextFor };
184
+ export { RAW_WRITING_PROMPT, WORKFLOW_TEXT, BRIEF_TEXT, LEAN_TEXT, SCENE_TEXTS, PROMPT_SCENES, promptTextFor };
@@ -511,8 +511,10 @@ export function judgeAgainstBaseline(metrics, baseline, tolerance) {
511
511
  outOfBand,
512
512
  outCount: outOfBand.length,
513
513
  skippedDims,
514
+ // v5.1.0(降本/防僵化):措辞从「需修正」改为「先判断是否为有意为之」——容差带是 1.5σ(作者自身波动),
515
+ // 六维里至少一维出带是常见现象;旧措辞会让模型把正常波动当成必须修的缺陷,越改越僵。
514
516
  summary: (outOfBand.length === 0
515
517
  ? (skippedDims.length === 0 ? "全部维度在容差带内 ✓" : "无维度超出容差带")
516
- : outOfBand.length + " 个维度超出容差带,需修正") + skippedNote
518
+ : outOfBand.length + " 个维度超出容差带(先判断是否为有意为之,偏离明显或读起来别扭才改)") + skippedNote
517
519
  };
518
520
  }
package/mcp/README.md CHANGED
@@ -150,7 +150,7 @@ printf '%s\n' \
150
150
  | 5 | `novel_new_chapter` | 创建新章节文件(自动附风格基线 μ 摘要与原著锚包) |
151
151
  | 6 | `novel_import` | 批量导入原稿件(scan 预览 / apply 落盘) |
152
152
  | 7 | `novel_sentence_analysis` | 句式模式分析(九类句式、转移、节奏、情感曲线、风格指纹) |
153
- | 8 | `novel_sentence_config` | 查看/修改功能开关(enabled / autoAnalyze / 各工具开关 / promptScene 提示词场景) |
153
+ | 8 | `novel_sentence_config` | 查看/修改功能开关(enabled / autoAnalyze / 各工具开关 / `promptScene` 提示词场景 / `leanWorkflow` 精简工作流) |
154
154
  | 9 | `novel_style_check` | 章节风格自检(与全书基线对比,输出偏差与锚段) |
155
155
  | 10 | `novel_plot` | 伏笔/剧情线登记表(open 待回收 / done 已回收);新增 `action:"graph"` 结构视图(伏笔埋设跨度 / 人物连续缺席 / 剧情线空档 / 时间线顺序 / 大纲对照,只读) |
156
156
  | 11 | `novel_settings` | 五张设定表(人物/地点/道具/时间线/世界观用语规范) |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-novel-writer",
3
- "version": "5.0.0",
4
- "description": "小说写作助手插件(v5.0.0):句式/情感/意象分析、伏笔设定管理、本地语义检索、氛围光谱、风格画像报告、文笔六维基线带、原创模式与创作资料、写作哨兵(衔接/OOC/大纲);附 stdio MCP 服务器(Claude Desktop / Cursor 可用)。",
3
+ "version": "5.1.0",
4
+ "description": "小说写作助手插件(v5.1.0):句式/情感/意象分析、伏笔设定管理、本地语义检索、氛围光谱、风格画像报告、文笔六维基线带、原创模式与创作资料、写作哨兵(衔接/OOC/大纲);附 stdio MCP 服务器(Claude Desktop / Cursor 可用)。",
5
5
  "mcpName": "io.github.siweina/dsh-novel-writer",
6
6
  "type": "module",
7
7
  "main": "lib/index.js",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.siweina/dsh-novel-writer",
4
4
  "title": "dsh-novel-writer — 网文写作体检",
5
5
  "description": "Offline toolkit: 18 tools for Chinese web-novel style, plot-thread and continuity audits.",
6
- "version": "5.0.0",
6
+ "version": "5.1.0",
7
7
  "repository": {
8
8
  "url": "https://github.com/siweina/dsh-novel-writer",
9
9
  "source": "github"
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "dsh-novel-writer",
16
- "version": "5.0.0",
16
+ "version": "5.1.0",
17
17
  "runtimeHint": "npx",
18
18
  "packageArguments": [
19
19
  {
@@ -34,12 +34,12 @@ whenToUse: 用户提到小说、网文、章节、续写、改稿、文风、句
34
34
  - **总开关 `enabled=false`**:`novel_sentence_analysis` 返回桩结果(不抛错)、`novel_style_check` 报错拒绝执行——准确语义见「情感可信度与 AI 复核」一节的开关说明;
35
35
  - 情感词典已去噪("叹气"不算"气"、"不害怕"不计)、祈使/环境分类已增强。
36
36
 
37
- ## 风格自检(v0.6.0 新增,v3.7.0 起强制)
37
+ ## 风格自检(v0.6.0 新增;**v5.1.0 起改为按需,不再是强制步骤**)
38
38
 
39
- - **续写前必须读基线**:动笔前调用 `novel_style_report`(完整版,包含每维 μ/σ/容差带;或 brief 拿 μ 摘要)确认本书六维风格基线;`novel_new_chapter` 创建章节时会自动附基线 μ 摘要——若发现与自己从正文中感受到的文风不符,以基线数据为准。
40
- - **v3.8.0 锚包写作**:report / new_chapter 返回的 `anchors`(原著代表性段落)与 `skeletons`(句式骨架)是风格锚——动笔时**照锚段的味道与句式形状写**,数字基线只做事后校验;不要试图把数字翻译成写作规则(语言概括有损且模型不可执行)。
41
- - **写完必须对照**:每写完一章调用 `novel_style_check`:本章 vs 全书其余章节 → 相似度 + 偏差清单 + 建议;
42
- - **强制判定(v3.8.0 对照修正)**:verdict 低于 high,或任一维度出带(metric verdicts 含 out)时,按返回的 `fixAnchors` 原著锚段**逐句对照修正跑偏部分,不得整章重写**;确属情节需要则说明原因;
39
+ - **基线不用专门去取**:`novel_chapter_brief` 与 `novel_new_chapter` 都会直接附上六维基线(μ/σ/容差带)。需要完整报告时才调用 `novel_style_report`(长书建议 `brief=true` 拿 μ 摘要)。
40
+ - **锚段只校准语感(v5.1.0 修正)**:`anchors`(原著代表性段落)读一遍记住味道即可;**不要照 `skeletons`(句式骨架)造句、不要套用它们的句式模板**——骨架只在写不出该类型句子时参考。目标是「像这位作者写的」,不是「像这位作者的句子拼出来的」。
41
+ - **写完建议自检(非强制)**:`novel_style_check` 给出相似度 + 偏差清单 + 六维对照(带内 ✓ / 出带 ⚠)。
42
+ - **修改判据(v5.1.0 修正)**:只有相似度明显偏低、或某一维偏离达到 **2 倍容差**以上、或那一处读起来确实别扭时,才按返回的 `fixAnchors` 原著锚段**只改那几处**(不得整章重写)。**单一维度轻微出带属正常波动**(1.5σ 带宽下六维里至少一维出带是常见现象),**不要为对齐数字改文**;确属情节或文体需要,说明理由即可。
43
43
  - 对照结果应体现在续写说明中(偏差维度 + 是否修正)。
44
44
 
45
45
  ## 设定管理(v0.8.0 新增)
@@ -70,19 +70,19 @@ whenToUse: 用户提到小说、网文、章节、续写、改稿、文风、句
70
70
  > **v5.0.0 起:先试 `novel_chapter_brief`。** 它**一次调用**就能拿到下面第 1–3 步的全部材料(上一章结尾原文、本章大纲方向、相关人物、待回收伏笔含埋设距离、世界观用语规范、六维基线、原著锚段与句式骨架、本章禁用清单)。只有当它返回的 `degraded` 里列出了缺项时,才需要按 1–3 步手工补读。
71
71
 
72
72
  1. 先通读最近的章节(必要时读完全书),保持:人物设定、时间线、情节伏笔、文风、已提炼的关键词一致;
73
- 2. **读风格基线(强制)**:调用 `novel_style_report` 获取六维基线(μ/σ/容差带),记录基线值——这是续写文风的硬基准;
73
+ 2. **读风格基线(按需)**:基线通常已随开写包 / 建章返回;需要完整报告时才调用 `novel_style_report`(长书可 `brief=true`)。它是**事后校验**的参照,不是写作规则;
74
74
  3. `novel_plot list` 查看未回收伏笔;
75
- 4. 动笔前简述:上一章结尾状态 → 本章目标 → 写作计划(计划中说明如何贴合基线六维);
75
+ 4. 动笔前简述:上一章结尾状态 → 本章目标 → 写作计划(说清要写什么即可,不必逐维对齐基线);
76
76
  5. 用 `novel_new_chapter` 创建新章节文件(返回会自动附带全书风格基线 μ 摘要,动笔对照),或直接用 `write` 工具写入;
77
77
  6. 正文用中文,保持与原文一致的叙事风格与细节密度;句式分析开启时参考指纹与节奏序列,僵硬时回归自然表达;
78
- 7. **写完强制对照基线**:调用 `novel_style_check`——verdict 低于 high 或任一维度出带(out)必须修正,或说明情节需要;然后 `novel_plot add` 登记新伏笔、`novel_outline hook` 回填本章钩子。
78
+ 7. **写完建议自检(非强制)**:`novel_style_check` —— 只有偏离明显(≥2 倍容差)或那一处读起来确实别扭时才逐句修正,不要为对齐数字改文。收尾**按需**:确属本章新埋的伏笔才 `novel_plot add`;钩子与摘要在章节稳定后再回填(`novel_outline hook` / `novel_summary`)。想进一步省 token,可在侧边栏打开**「精简工作流」**。
79
79
 
80
80
  ## 开写包(v5.0.0 新增,`novel_chapter_brief`)
81
81
 
82
82
  动笔前的一次调用材料装配。核心动机:这些材料原先分散在 6–8 次工具调用里、靠提示词叮嘱模型去取,**漏读正是文风漂移与设定矛盾的头号来源**。
83
83
 
84
84
  - 参数:`book`(必填)、`chapter`(默认 `next` = 当前最大章号 + 1,文件可尚未创建;也可给章号 / 文件名 / 标题子串)、`budget`(`compact` 默认 / `full`)、`root`。
85
- - 返回:`anchor`(上一章结尾原文,即**承接口**)|`previousHook`|`outlineDirection`|`characters`|`openPlots`(每条带 `distance` = 埋了多久没回收)|`worldview`|`baseline`(六维 μ/σ/容差)|`anchors` + `skeletons`(照这个味道与形状写)|`lastVerdict`|`avoid`(**本章禁用清单**:已回收伏笔 ∩ 本章方向行的关键词、世界观禁词、已退场角色)|`plan.checklist`|`degraded`。
85
+ - 返回:`anchor`(上一章结尾原文,即**承接口**)|`previousHook`|`outlineDirection`|`characters`|`openPlots`(每条带 `distance` = 埋了多久没回收)|`worldview`|`baseline`(六维 μ/σ/容差)|`anchors` + `skeletons`(**锚段只校准语感,不要照骨架造句**)|`lastVerdict`|`avoid`(**本章禁用清单**:已回收伏笔 ∩ 本章方向行的关键词、世界观禁词、已退场角色)|`plan.checklist`|`degraded`。
86
86
  - **只读**:不写任何文件(仅复用既有分析缓存)。
87
87
  - **优雅降级**:没有大纲 / 没有伏笔 / 没有设定表 / 书只有 1 章 —— 一律返回空值并在 `degraded` 说明原因,**绝不抛错**(唯一例外:书名目录不存在)。
88
88
  - 用法要点:拿到之后**先读锚段再动笔**;`baseline` 的数字只做事后校验,不要把数字翻译成写作规则;`avoid` 里的东西不要写。
@@ -113,16 +113,19 @@ whenToUse: 用户提到小说、网文、章节、续写、改稿、文风、句
113
113
 
114
114
  ## 提示词档位与场景(v5.0.0 新增)
115
115
 
116
- 系统提示词由**两个正交维度**决定,都在侧边栏「写作助手功能」面板调整,也可用 `novel_sentence_config` 读写:
116
+ 系统提示词由**三个维度**决定,都在侧边栏「写作助手功能」面板调整,也可用 `novel_sentence_config` 读写:
117
117
 
118
118
  | 维度 | 取值 | 作用 |
119
119
  |---|---|---|
120
120
  | 档位 `systemPromptMode` | `off` / `brief` / `full` | **注入多少**:不注入 / 一句话 / 完整流程 |
121
121
  | 场景 `promptScene` | `general` / `writing` / `revising` / `auditing` / `setup` | **注入什么**:通用 / 写新章 / 改稿 / 审计(只体检不改稿)/ 建资料(只维护设定与创作资料) |
122
+ | 精简工作流 `leanWorkflow` | `true` / `false`(默认 `false`) | **要不要催促动作**(v5.1.0 新增):打开后注入 380 字符的精简档,只保留"工具按需调用"一条原则,去掉自检/复测/登记的全部催促 |
122
123
 
123
124
  - 场景**仅 `full` 档下生效**;`brief` 档与场景无关。
124
- - **向后兼容**:`general` 就是 4.x 的那段通用工作流,没有 `promptScene` 键的老状态文件自动落到 `general`,**既有工作流不变**(本版只在其中补入三条新工具的入口指引,共 +7 行,其余逐字沿用 4.x)。
125
- - 场景只改变注入的提示词内容,**不影响任何工具的能力**。
125
+ - **精简工作流优先于档位与场景**:打开后无论选了什么档位/场景,注入的都是精简档(`off` 除外);它**任何时候都能切换**,不受 `full` 档限制。
126
+ - 精简工作流打开时,`novel_style_report` 与 `novel_sentence_analysis` 在调用方**未显式指定 `brief`** 时默认走精简输出(省 token)。
127
+ - **向后兼容**:`general` 就是 4.x 的那段通用工作流,老状态文件(没有 `promptScene` / `leanWorkflow` 键)自动落到 `general` + `false`,**既有工作流不变**。
128
+ - 三者只改变注入的提示词内容与报告精简度,**不影响任何工具的能力**。
126
129
 
127
130
  ## 注意事项
128
131
 
@@ -213,11 +216,11 @@ novel_sentence_analysis 的 emotion.quantification.semanticImplicit(semanticEm
213
216
  - 按目标字数选档:超短篇(≤1万字/一次列全) / 短篇(1-10万/10章批) / 中篇(10-30万/20章批) / 长篇(30-80万/30章批) / 超长篇(80万+/30章批);用户没报字数 → 从 10 章/批起步;
214
217
  - 一批写完后若用户想继续 → 先回填更新创作设定(角色/线络变化),再列下一批;批次可升档(10→20→30)。
215
218
 
216
- ### 强制流程(原创/续写都适用)
217
- 1. 动笔前:novel_outline read status(几十字快照)→ 再 read outline(本章方向行)→ 动笔;不够时按需读人物/设定;
218
- 2. 写完一章:novel_outline hook(number/body)回填结尾钩子;新出场且有戏份的角色 → novel_outline character(minor 登记,升咖改 main);
219
- 3. 每章写完可刷新 status(进度/下章方向);下一章开头从上一章钩子接续(时间/场景变化必须写过渡句);
220
- 4. read status/outline 会提示「未回填钩子章节」——先补钩子再写下一章;
219
+ ### 创作资料维护节奏(原创/续写都适用;**v5.1.0 起改为按需,不再逐章强制**)
220
+ 1. 动笔前(需要时):`novel_outline read status` 看几十字快照;本章方向不明时再 `read outline`;人物/设定按需读,不必每次全读;
221
+ 2. 写完一章:**有新出场且有戏份的角色**才 `novel_outline character` 登记(minor 登记,升咖改 main);`hook` 回填与 `status` 刷新可以等章节稳定后一次做完,不必每章一趟;
222
+ 3. 下一章开头从上一章钩子接续(时间/场景变化必须写过渡句);
223
+ 4. `read status` 会提示「未回填钩子章节」——**那是提醒,不是必须立刻补**;建议每几章统一补一次;
221
224
  5. 大纲只定方向(核心事件),**结尾钩子不预写**——写完回填,创作自由且衔接有据。
222
225
 
223
226
  ## 网文信号层(v2.2.0)