dsh-maoying 0.1.12 → 0.1.14

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 ADDED
@@ -0,0 +1,169 @@
1
+ # dsh-maoying (Maoying)
2
+
3
+ [中文](README.md) | English
4
+
5
+ **Maoying** literally means "the brush" — in *Maoying Zhuan* (毛颖传, "The Biography of the Brush"), Han Yu wrote a writing brush as a courtier with a fief and a title. dsh-maoying fits that brush into DSH (DeepSeek Harness): **a brush that manages a crew of helpers and guards a stack of manuscripts** — a web-novel writing assistant plugin, fully original, released under MIT.
6
+
7
+ Once installed, the main agent becomes the **editor-in-chief of your novel**: it runs a seven-phase workflow with a six-step writing method, delegating outlining, drafting, review, lore-keeping and research to five helpers who each carry only their own tools. The whole novel is a structured markdown/YAML `novel/` directory in the workspace (character cards, foreshadowing state machine, timeline, clue board, narrative memory) — agents read and write files directly, **no database, no API layer**. "Show all unresolved foreshadowing" is one line of grep.
8
+
9
+ ## Features
10
+
11
+ - **Editor-in-chief + five helpers**: the main agent plans, connects, reviews and advances states; the outliner (outline), writer (write), reviewer (review), lore keeper (lore) and researcher (research) each carry their own tools. Trivial errands like finding a file or checking a foreshadow status, the chief handles itself with glob/grep.
12
+ - **novel/ as a file database**: character cards, worldbuilding & science settings with three-color tags, relations graph, foreshadow ledger, timeline, narrative memory, clue board — all grep-friendly flat-key/top-level-array YAML, human-editable, git-versioned, no migrations, no ops.
13
+ - **Seven-phase workflow**: idea → setting → outline → writing → review → consistency → export. The phase lives in the `phase` key of project.yml; the chief advances it at each milestone and reports to you.
14
+ - **Writing six-step method**: continuity check → outline → draft → review → revision → finalization, looping per chapter; paired with the Collab four-step co-writing flow (setting co-creation → volume planning → golden three chapters → handoff loop) it takes a new book from zero to rolling updates.
15
+ - **Foreshadowing state machine**: planted → progressing → resolved / abandoned, with planting and payoff chapter numbers, expected-payoff scheduling and priority tiers — a long serial never drops a thread; past 15 active foreshadowing entries the chief proactively suggests cleanup.
16
+ - **Context on demand**: the chief reads index.yml for the big picture, then pulls only what the chapter at hand needs (previous chapter's ending ≈1500 chars + its outline + on-stage character cards + active foreshadow excerpts) — never the whole book.
17
+ - **Narrow tool surface**: the chief sees only "role delegation + internal retrieval + coordination"; helpers carry their line's tools (reviewer read-only, researcher web+notes only) — less per-request tool overhead and context weight.
18
+ - **Research**: the research helper searches the web for historical/geographic/domain material, files notes under `novel/research/`, separating fact from common lore from legend, with sources — a dividend of the DSH architecture.
19
+ - **Creator panel**: a full-page web panel from the DSH sidebar — Overview / Chapters / Characters / Foreshadowing / Relations / Timeline / Worldbuilding / Index Board / Novel Settings; full-page chapter reading (outline side-by-side + five-dimension fields + status flow), form-based project.yml editing, UI language (中文/English), six font-size steps, and automatic new-version detection.
20
+ - **CLI fallback**: `maoying init / stats / index / export / brief / check` — six zero-dependency commands for scaffolding, stats, index rebuild, export, per-chapter writing cards and static checks.
21
+
22
+ ## Quick Start
23
+
24
+ Prerequisites: **Node.js ≥ 18** and an installed DSH host (**0.1.7+**, see "Compatibility").
25
+
26
+ The official plugin channel — one command, then restart:
27
+
28
+ ```bash
29
+ dsh plugin --profile web add dsh-maoying
30
+ ```
31
+
32
+ **Restart DSH (`dsh web`)**: at startup the plugin installs the preset into `~/.dsh/.agent-presets/maoying/` and writes the managed declaration block into `~/.dsh/cordis.patch.yml` — no buttons to press. Pick "**毛颖创作 (Maoying)**" in the preset selector of a new session; to make it default, select it in Settings → Agent Presets.
33
+
34
+ For a development checkout (you want to modify the plugin or preset sources), link the repo directly; changes take effect after a restart:
35
+
36
+ ```bash
37
+ git clone https://github.com/lifangjin/dsh-maoying.git
38
+ dsh plugin --profile web add link:"$PWD/dsh-maoying"
39
+ ```
40
+
41
+ Only want the command-line tool, without the plugin? The zero-dependency CLI works standalone via `npx`, or as a global command (both npm channels, independent of the plugin):
42
+
43
+ ```bash
44
+ npx maoying init # run without installing
45
+ npm install -g dsh-maoying # or install a global maoying command
46
+ ```
47
+
48
+ ## How to Use
49
+
50
+ With the preset selected, just talk to the chief; it routes by the workflow itself:
51
+
52
+ **Start a new book (Collab four-step co-writing)**:
53
+
54
+ > I want to write an Eastern fantasy where the protagonist's golden finger is hearing objects speak and appraising treasures to level up.
55
+
56
+ The chief aligns genre and tone with you, delegates core character cards and worldbuilding to the lore keeper, volume planning to the outliner, then has the golden three chapters (world hook, protagonist motivation, core conflict) written and delivered.
57
+
58
+ **Per-chapter writing (Writing six-step method)**:
59
+
60
+ > Can we start chapter 3?
61
+
62
+ The chief runs the continuity check, has the outliner produce the chapter outline (with the five narrative dimensions), and after your sign-off sends it to the writer, then the reviewer (checklist + four-dimension consistency scan); issues go back to the writer; once accepted, the lore keeper does finalization upkeep — you only call the shots at decision points.
63
+
64
+ **Consistency pass**:
65
+
66
+ > Run a consistency check over the first 20 chapters.
67
+
68
+ The chief advances the phase to consistency; the reviewer scans four dimensions — conflicts / timeline / foreshadowing / settings — and returns issues ranked by severity.
69
+
70
+ A reply of "ok / sure / go on" counts as confirmation; the chief advances directly without echoing it back.
71
+
72
+ ## Creator Panel
73
+
74
+ Open "**毛颖创作台**" at the bottom of the DSH sidebar — a full-page cockpit for the human eye, beyond what agents do:
75
+
76
+ - **Chapters**: status filters + the writing queue; click a chapter for full-page reading — back button, outline side-by-side (Markdown rendered), read-only manuscript, five narrative-dimension fields, one-click status flow.
77
+ - **Novel Settings**: form-based editing of project.yml — title / author / genre / planned length / language / workflow phase / co-writing mode / synopsis; saving preserves the file's existing comments. If project.yml was changed externally after the form loaded (manual edits or an agent advancing the phase), saving is rejected with a prompt to re-edit from the latest content instead of overwriting.
78
+ - **Top bar**: current version always visible; a "new version ↗" badge links to the release page when npm has a newer one, next to an "Upgrade now" button that installs the latest release into the current DSH profile with pnpm (profile-directory installs only; a DeepSeek restart is required to take effect); the gear opens Settings — UI language (中文/English), six font-size steps (100%–150%), all applied immediately and remembered.
79
+
80
+ The panel only writes state (chapter status, project.yml, index.yml rebuilds), never prose — prose belongs to the author and the chief.
81
+
82
+ ## Co-writing Mode (Author Writes)
83
+
84
+ Not everyone wants AI ghostwriting. The `mode` key in project.yml decides who writes the prose — three modes in one line each: `ai-draft` (default) AI drafts via the Writing six-step method; `co-write` assigns a writer per chapter, and the chief asks who writes before touching the pen; `author-draft` the author writes the whole book, and Maoying only preps, reviews and keeps the books — never ghostwrites.
85
+
86
+ The author-draft loop — you just write, hand it over, and the chores are taken care of:
87
+
88
+ > Chapter 5 is done, here it is. (paste the text, or just say chapter X is done and point at a file)
89
+
90
+ After receiving a manuscript you get three things: a **review checklist** (issues only, no rewriting — applying them is up to you), a **ledger backfill** (foreshadowing / timeline / relations / memory / clue board recorded from what your text actually does, even off-plan changes, tagged `backfill`), and the **next chapter outline** (`novel/outlines/vXX-cXXX.md`, ready to write from).
91
+
92
+ Companion commands: `maoying brief <dir> <chapter>` packs a per-chapter writing card (outline + previous ending + character cards + active foreshadowing — carry it to any editor or AI), and `maoying check <dir>` runs a zero-model static check (frontmatter completeness, foreshadow chapter references, timeline ordering — no model calls; LLM review goes through the chief).
93
+
94
+ ## Methodology Skills
95
+
96
+ The plugin ships 4 web-novel methodology skills. At startup the installer drops four `maoying-*` directories under `~/.dsh/skills/` (i.e. `<home>/skills/`) where the host's skill panel lists them; the outliner, writer, reviewer and lore keeper load them by scenario — no manual steps:
97
+
98
+ | Skill | One-liner |
99
+ |---|---|
100
+ | maoying-golden-opening | Golden three chapters: role split of the first three chapters (world hook → motivation → conflict detonation), the "something happens in the first 300 words" rule, information-release discipline and a self-check list |
101
+ | maoying-beat-pacing | Payoff pacing: a payoff-type library, suppression-release ratios, chapter/arc/book pacing curves and chapter-end hook selection |
102
+ | maoying-review-checklist | Review checklist: six layers (language / pacing / characterization / logic / foreshadowing / spoilers) with P0/P1/P2 severity |
103
+ | maoying-foreshadow-discipline | Foreshadow management: register on planting, pacing & scheduling, the "surprising yet reasonable" payoff bar, and ledger cleanup thresholds |
104
+
105
+ The installer only touches `maoying-*` prefixed directories; your own skills under `~/.dsh/skills/` are untouched, byte for byte. After uninstalling the plugin, remove the skill directories manually (all four together):
106
+
107
+ ```bash
108
+ dsh plugin --profile web remove dsh-maoying
109
+ rm -rf "${DSH_HOME:-$HOME/.dsh}/skills/"maoying-*
110
+ ```
111
+
112
+ ## The novel/ Data Spec
113
+
114
+ The `novel/` directory is the plugin's entire database: project.yml (metadata + seven-phase state), volumes.yml, chapters/ (frontmatter five narrative dimensions + prose), characters/ (character cards), world/, science/ (three-color settings), relations.yml, foreshadows.yml (the state machine), timeline.yml, memories.yml (with appearance records and the promise ledger), clue-board.yml, research/, exports/. Full formats, examples and design rationale:
115
+
116
+ - Quick reference: `novel/README.md` after project init (template source: [templates/novel/README.md](templates/novel/README.md))
117
+ - Full spec: [docs/spec.md](docs/spec.md) (why not SQLite, five-dimension field definitions, foreshadow state-machine semantics, appearance-record conventions)
118
+
119
+ ## CLI
120
+
121
+ ```bash
122
+ maoying init [dir] # scaffold novel/ (default ./novel; existing files are always skipped, never overwritten)
123
+ maoying stats [dir] # per-chapter CJK counts, status breakdown, current phase
124
+ maoying index [dir] # rebuild index.yml from volumes.yml + chapters/ (derived file)
125
+ maoying export [dir] # export the book: --format md|txt|epub (default md), --out <dir> to change destination
126
+ maoying brief [dir] <chapter> # pack a per-chapter writing card (outline + previous ending + character cards + active foreshadowing); --out to write to disk, stdout by default
127
+ maoying check [dir] # static check (no model calls): frontmatter / enums / chapter numbers / foreshadow refs / timeline years / relation-card refs
128
+ ```
129
+
130
+ Export examples:
131
+
132
+ ```bash
133
+ maoying export novel # Markdown, into novel/exports/
134
+ maoying export novel --format txt # plain text
135
+ maoying export novel --format epub # EPUB 3 (zero-dependency packaging, importable into readers)
136
+ maoying export novel --format epub --out ~/Downloads # different destination
137
+ ```
138
+
139
+ The output directory defaults to `<dir>/exports/`, file name `<title>-<YYYYMMDD>.<ext>` (title from project.yml); chapter parsing shares the same logic as stats/index, so frontmatter never leaks into prose.
140
+
141
+ Zero dependencies (node:fs/path/url/crypto), runs from any cwd. Install one of three ways: `npm install -g dsh-maoying`, on-demand `npx maoying <command>`, or `npm link` on a dev machine (repo checkout, edits take effect immediately). With none of these, `node <plugin dir>/bin/maoying.mjs` is equivalent (a dsh plugin install also drops the package into the profile, so you can point there directly). In day-to-day writing the lore keeper rebuilds index.yml directly; the CLI is mainly for humans.
142
+
143
+ ## Uninstall
144
+
145
+ ```bash
146
+ dsh plugin --profile web remove dsh-maoying
147
+ rm -rf "${DSH_HOME:-$HOME/.dsh}/.agent-presets/maoying"
148
+ rm -rf "${DSH_HOME:-$HOME/.dsh}/skills/"maoying-*
149
+ ```
150
+
151
+ On DSH 0.1.7+ also delete the dsh-maoying managed block in `~/.dsh/cordis.patch.yml` (the whole section between `# --- dsh-maoying preset managed (auto-generated; do not edit) ---` and `# --- end dsh-maoying preset managed ---`, markers included), so no declaration lines point at a deleted directory. The `novel/` directory is your manuscript — uninstalling never touches it.
152
+
153
+ ## Compatibility
154
+
155
+ At startup the plugin reads the host version (`<home>/node_modules/@deepseek-ai/dsh/package.json`) and picks the delegation-line shape automatically — **after a host upgrade, a restart is all it takes; no reinstall**:
156
+
157
+ - **DSH 0.1.7 – 0.2.1-rc.1 → v1 inline shape**: the 5 role delegation lines inline persona and toolFilter; the managed block is 1 declaration line.
158
+ - **DSH 0.2.1-rc.2+ → v2 AgentPreset shape**: from that version dsh-tool-subagent rejects inline persona/toolFilter, so the plugin switches to [presets/maoying/agent.cordis.v2.yml](presets/maoying/agent.cordis.v2.yml) — the managed block writes 6 declaration lines (chief-in-chief preset + 5 role presets: outliner / writer / reviewer / lore keeper / researcher); the chief's delegation line points at the role presets via `agentPreset`, and each role's tool surface is carried by the preset's entity tool lines (reviewer read-only, researcher with web search).
159
+ - **Role presets appear in the preset selector** (the host has no hiding mechanism for presets); their names carry "(内部)" to mark them: in daily writing they are picked automatically via chief delegation, but you can also select a writer/researcher directly for a session without the chief.
160
+ - Both shapes share persona text byte-for-byte from one source ([test/composition.test.mjs](test/composition.test.mjs) guards it); if the host version can't be read, the plugin falls back to v1 conservatively.
161
+ - The ≤0.1.6 directory-scan track: not supported (the host dropped it).
162
+
163
+ ## License
164
+
165
+ [MIT](LICENSE) © 2026 lifangjin
166
+
167
+ ## Acknowledgments
168
+
169
+ - "Maoying" comes from Han Yu's *Maoying Zhuan* (毛颖传): a writing brush personified, with a fief and a title.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # dsh-maoying(毛颖)
2
2
 
3
+ 中文 | [English](README.en.md)
4
+
3
5
  毛颖者,笔也——韩愈作《毛颖传》,把一支毛笔写成有封地有爵位的臣子。dsh-maoying 把这支笔装进 DSH(DeepSeek Harness):**一支管着一班帮手、守着一摞书稿的毛笔**——网文创作辅助插件,全部实现原创,MIT 发布。
4
6
 
5
7
  装上之后,主 agent 变身**网文主编(创作总监)**:带七阶段工作流与六步写作法,把大纲、正文、审校、设定维护、素材考据分别委派给五个只带本行工具的帮手;整部小说就是工作区里的 `novel/` 结构化 markdown/YAML 目录(角色卡、伏笔状态机、时间线、线索板、叙事记忆)——agent 直接读写文件,**没有数据库,没有 API 层**。查「所有未回收伏笔」就是 grep 一行。
@@ -72,8 +74,8 @@ npm install -g dsh-maoying # 或装成全局 maoying 命令
72
74
  DSH 左侧栏底部点「**毛颖创作台**」打开全页面板(改文件、跑流程之外,给肉眼看的驾驶舱):
73
75
 
74
76
  - **章节**:状态筛选 + 按单写作清单;点章节进整页阅读——返回按钮、章纲对照(Markdown 渲染)、只读正文、五维叙事字段、一键状态流转。
75
- - **小说设置**:project.yml 的书名/作者/题材/篇幅/语言/工作流阶段/共创模式/简介表单化编辑,保存保留文件里原有注释。
76
- - **顶栏**:当前版本号常驻;npm 上有新版时亮「有新版 ↗」徽章直连发布页;齿轮打开系统设置,字号五档(100%–200%)整体缩放并记忆。
77
+ - **小说设置**:project.yml 的书名/作者/题材/篇幅/语言/工作流阶段/共创模式/简介表单化编辑,保存保留文件里原有注释;表单载入后 project.yml 被外部(作者手改 / agent 推进阶段)改过时拒绝保存并提示重改,不覆盖人工改动。
78
+ - **顶栏**:当前版本号常驻;npm 上有新版时亮「有新版 ↗」徽章直连发布页,旁边「一键升级」按钮直接用 pnpm 把最新包装进当前 DSH 档(仅支持 profile 目录安装;装完需重启 DeepSeek 才生效);齿轮打开系统设置,界面语言中文/English 可切、字号六档(100%–150%)整体缩放,均立即生效并记忆。
77
79
 
78
80
  面板只写状态(章节状态、project.yml、index.yml 重建),不改正文——正文永远归作者与主编。
79
81
 
@@ -39,17 +39,19 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
39
39
  |---|---|---|---|
40
40
  | `client.css` | GET | — | text/css(lib/base.css 现读,no-cache) |
41
41
  | `update-check` | GET | — | `{ ok, current, latest, updateAvailable, releaseUrl, checkedAt, error, source }`——新版检测(npm registry → npmmirror → GitHub 三级源,1h 缓存 + 7 天 stale-if-error,全败静默降级为 latest=null / error 非空) |
42
+ | `self-update` | POST | — | `{ ok, from, to, restartRequired: true }` 或 `{ ok: false, kind, error }`——面板内一键升级:安装形态/pnpm 探测通过后跑 `pnpm --dir <profileDir> add dsh-maoying@latest`(180s 超时,无 shell)。仅支持 `<dshHome>/profiles/<profile>/node_modules/dsh-maoying` 真实目录安装;symlink/dev 检出(dev-link)与非 profile 布局(unsupported-layout)、pnpm 缺失(pnpm-missing)、超时(timeout)、pnpm 非零退出(pnpm-failed)一律 200 + ok:false。**装完只换磁盘包,运行中进程仍是旧代码,restartRequired 恒 true——重启 DeepSeek 后生效** |
42
43
  | `host-debug` | GET | — | `{ ok, hostVersion, trace, shape, registry, runtime }`——现场诊断(见下节) |
43
44
  | `workspaces` | GET | — | `{ ok, workspaces: [{ path, name, hasNovel }] }`——workspaceRegistry.list() 逐根探测 `<root>/novel/project.yml` |
44
45
  | `state` | GET | `path=<root>` | 全量快照(见 §3 契约) |
46
+ | `check` | GET | `path=<root>` | `{ ok, problems: [{ level, file, message }] }`——静态体检:与 CLI check 同源的 checkProject,读章节 frontmatter 原文(快照会把越枚举 status 归一成 pending,这里透出原始问题清单;level ∈ error/warn,可为空数组),概览「静态体检」黄条取数 |
45
47
  | `chapter` | GET | `path`, `file`(chapters/ 下文件名) | `{ ok, file, frontmatter, body, outline }`——正文全文 + 对应章纲(`{ file, words, body }`,outlines/ 无对应文件时 null),章节抽屉用 |
46
48
  | `chapter-status` | POST | `{ path, file, status }` | `{ ok }`——frontmatter status 行原地替换;status 必须 ∈ pending/writing/review/accepted |
47
49
  | `foreshadow-status` | POST | `{ path, id, status }` | `{ ok }`——foreshadows.yml 对应条目 status 行替换;∈ planted/progressing/resolved/abandoned |
48
50
  | `phase` | POST | `{ path, phase }` | `{ ok }`——project.yml phase 行替换;七态枚举 |
49
- | `project-save` | POST | `{ path, values }`(8 平键,title/author/genre/length/language/phase/mode/description) | `{ ok }`——mutateProject 逐键改写保行尾注释、缺键按模板补齐;phase/mode 越枚举与空书名 422 |
51
+ | `project-save` | POST | `{ path, values, base_hash? }`(values 为 8 平键 title/author/genre/length/language/phase/mode/description;base_hash 为表单载入时的 project.yml 指纹,即快照 `project_hash`) | `{ ok, project_hash }`——mutateProject 逐键改写保行尾注释、缺键按模板补齐;phase/mode 越枚举与空书名 422;base_hash 与写入前现读原文不一致 → 409「外部修改冲突」拒写不落盘(人工改动保护:表单载入后文件被作者/agent 改过即拒绝静默覆盖;缺省 base_hash 跳过检查,兼容旧调用方)。project_hash 为写后新指纹,客户端可作下一轮基线 |
50
52
  | `rebuild-index` | POST | `{ path }` | `{ ok, chapters }`——照 CLI index 同一逻辑重建 index.yml |
51
53
 
52
- 共 11 条。写操作全部「行级原地替换」:只在 frontmatter/目标 yml 的对应行上做字符串手术,写前读、写后校验(替换计数 ≠1 即 409 拒写并报错),不做整文件重排——把对 git diff 的扰动压到最小,也与 agent 的写法兼容。
54
+ 共 13 条。写操作全部「行级原地替换」:只在 frontmatter/目标 yml 的对应行上做字符串手术,写前读、写后校验(替换计数 ≠1 即 409 拒写并报错),不做整文件重排——把对 git diff 的扰动压到最小,也与 agent 的写法兼容。(`self-update` 是例外:它不碰小说文件,跑的是 pnpm 安装。)
53
55
 
54
56
  ### host-debug 端点(现场诊断)
55
57
 
@@ -71,13 +73,14 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
71
73
  "root": "<workspace root>",
72
74
  "novelDir": "<root>/novel",
73
75
  "found": true, // false 时其余字段缺省,面板显示「未初始化」引导
76
+ "project_hash": "9f86d0…", // sha256(project.yml 原文) hex——面板保存回传 project-save 的 base_hash 做旧版检测(人工改动保护),读不到为 null;found:false 时恒为 null
74
77
  "project": { "title","author","genre","length","language","phase","mode","description" }, // mode: ai-draft|co-write|author-draft(五期)
75
78
  "chapters": [{ "file","volume","num","title","status","words","summary",
76
79
  "cognitive_frame","emotional_anchor","world_texture",
77
80
  "suspense","interpersonal_tension" }], // 按 volume,num 升序;words 为 CJK 计数
78
81
  "characters": [{ "file","name","age","gender","role","appearance","personality",
79
82
  "background","motivation","arc","markers" }], // markers 数组,缺省 []
80
- "relations": [{ "a","b","type","description","intensity","started","ended" }],
83
+ "relations": [{ "a","b","type","description","intensity","started","ended" }], // a/b:relations.yml 里是角色 slug,出快照时对得上角色卡的端点归一成 name(关系图按 name 找节点),对不上原样保留
81
84
  "foreshadows": [{ "id","title","status","priority","planted_chapter",
82
85
  "expected_resolve","resolved_chapter","notes","source" }], // source: plan|backfill,缺省 plan
83
86
  "timeline": [{ "year","title","description","importance","chapter" }],
@@ -105,7 +108,7 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
105
108
 
106
109
  九个 tab(顺序即默认排布,第 9 个为后续迭代新增):
107
110
 
108
- 1. **概览**:project 卡(书名大字 + 类型/篇幅/语言 pills + 阶段徽章 + 简介);统计卡(总字数/章节数/状态分布条);最近更新(chapters 按 mtime 前 5);warnings 黄条。
111
+ 1. **概览**:project 卡(书名大字 + 类型/篇幅/语言 pills + 阶段徽章 + 简介);统计卡(总字数/章节数/状态分布条);最近更新(chapters 按 mtime 前 5);warnings 黄条;静态体检黄条(check 路由问题清单,见 §4.1「人工改动保护 + 静态体检黄条」(0.1.14)条目)。
109
112
  2. **章节**:表格(卷-章/标题/状态徽章/字数/梗概),状态筛选 pills;表格上方「按单写作」区块——有章纲且 status=pending 的章按序列出(章纲节选 + 状态徽章,共创模式的主工作清单);行点击开整页阅读视图(0.1.12 起替代原右侧抽屉——fixed 侧弹层会被 DSH 侧边栏遮挡):返回按钮 + GET chapter 正文(只读,等宽滚动)+ 章纲对照栏(左右分栏或折叠段)+ 复制文件路径按钮 + 五维字段卡 + 状态流转按钮(当前态 → 下一态一键,pending→writing→review→accepted)。
110
113
  3. **角色**:卡墙(姓名/role 徽章四色/年龄性别/性格一句/动机一行),点击看全卡(含 arc/markers/background)。
111
114
  4. **伏笔**:四列看板(埋设/推进/已回收/弃用),卡片含 priority 色点(high 红/normal 灰/low 淡)、`source: backfill` 的「回填」小徽章(账本回填来的条目,区别于写前规划)、埋设章→预期回收章、notes 全文直显(0.1.10 去掉两行截断);卡片按钮:推进到下一状态(planted→progressing→resolved;abandoned 只能从看板拖不动、按钮给「弃用」)。列头计数。
@@ -116,14 +119,18 @@ webServer.register({ kind: 'prefix', path: '/api/maoying', handler: async (req,
116
119
 
117
120
  状态徽章色(全局统一):pending 灰 / writing 蓝 / review 橙 / accepted 绿;phase 七态沿用七色 pill。文案全中文、禁 emoji 图标(用 ui-primitives 图标或纯 CSS)。
118
121
 
119
- ## 4.1 后续迭代记录(0.1.10 – 0.1.12)
122
+ ## 4.1 后续迭代记录(0.1.10 – 0.1.14)
120
123
 
121
124
  - **MaskPortal**:抽屉/弹层统一 portal 到 `document.body`——面板容器挂在宿主会话中栏,中栏 transform/overflow 会把 `position: fixed` 劫持成相对自身并连同裁剪(桌面端「点了没反应」实测病灶);无 createPortal 的老宿主退回原地渲染。
122
125
  - **顶栏版本区**:当前版本号常驻;`update-check` 路由(模仿 dsh-paoding:npm registry → npmmirror → GitHub 三级源,1h 缓存 + 7 天 stale-if-error,全败静默)发现新版时亮「有新版 ↗」徽章。
123
- - **系统设置**:齿轮弹窗,字号五档(100/120/150/180/200%)——面板根与 portal 节点读同一 `--myt-zoom` 变量整体 zoom,localStorage 记忆。
126
+ - **系统设置**:齿轮弹窗,字号六档(100/110/120/130/140/150%)——面板根与 portal 节点读同一 `--myt-zoom` 变量整体 zoom,localStorage 记忆。
127
+ - **界面语言**:系统设置可选中文/English——全部 UI 文案走 `I18N` 词典(zh/en 各 211 条,`tt`/`tf` 取词 + `{x}` 插值,zh 值与历史硬编码逐字一致保证中文界面零回归),切换立即生效整树重渲并 localStorage 记忆;语言自称(中文/日本語)与侧栏入口品牌名不翻译。
124
128
  - **Markdown 渲染**:零依赖渲染器(先整体转义再变换),章纲对照与世界观/科学设定描述不再裸显 md 源码;正文手稿仍走 pre。
125
129
  - **章节整页阅读**:替代右侧抽屉(侧边栏遮挡问题),返回按钮 + 章纲对照 + 五维 + 状态流转原位保留;阅读态隐藏 tab 条。
126
130
  - **小说设置 tab**(第 9 个):project.yml 平键表单化编辑,`POST project-save` → `mutateProject` 逐键改写保行尾注释、缺键按模板补齐、phase/mode 越枚举与空书名 422。
131
+ - **字号六档改百分比**(0.1.13):档位从中文名(小/标准/大…)改为 100/110/120/130/140/150% 直读,旧 localStorage 档位值回落 100%。
132
+ - **人工改动保护 + 静态体检黄条**(0.1.14):快照顶层新增 `project_hash`(project.yml 原文 sha256,读不到为 null)——小说设置保存回传 `base_hash`,表单载入后 project.yml 被作者手改或 agent(如 mutatePhase 推进 phase)动过时 `project-save` 409 拒写并报「外部修改冲突」,客户端重拉快照把表单放回磁盘真值,不再静默覆盖同名键丢人工改动;概览新增「静态体检」黄条(GET `check`,与 CLI check 同源的 checkProject,透出快照归一掩盖的越枚举等问题,含 error 级时整条转错误色)。路由 11 → 12 条;I18N zh/en 各 209 → 211 条(check.title / check.fullHint)。
133
+ - **面板内一键升级**(0.1.14):`POST self-update`(lib/self-update.mjs,依赖全部可注入)——顶栏新版徽章旁加「一键升级」按钮,安装形态(`<dshHome>/profiles/<profile>/node_modules/dsh-maoying` 真实目录,symlink/dev 检出一票否决)与 pnpm 可执行性(PATH → homebrew/pnpm 官方安装位候选)双闸通过后跑 `pnpm --dir <profileDir> add dsh-maoying@latest`(180s 超时、无 shell、模块级 in-flight 去重),成功后失效版本检测缓存并提示「已安装 {to},重启 DeepSeek 后生效」——**装完只换磁盘包,重启才生效,UI 文案照实说**;安装中按钮禁用(前端该请求超时放宽到 200s),「已安装,待重启」后徽章收起。路由 12 → 13 条;I18N zh/en 各 211 → 219 条(header.upgrade* / toast.selfUpdate.*)。
127
134
 
128
135
  ## 5. 验收基线
129
136
 
package/index.mjs CHANGED
@@ -55,6 +55,7 @@ import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readdirSync,
55
55
  import { createHash } from 'node:crypto'
56
56
  import { pathToFileURL } from 'node:url'
57
57
  import { getVersionInfo } from './lib/version.mjs'
58
+ import { selfUpdate } from './lib/self-update.mjs'
58
59
  import { readFile } from 'node:fs/promises'
59
60
  import { createRequire } from 'node:module'
60
61
  import os from 'node:os'
@@ -72,6 +73,8 @@ import {
72
73
  rebuildIndex,
73
74
  resolveNovelRoot,
74
75
  } from './lib/panel-core.mjs'
76
+ // 静态体检(check 路由)与 CLI check 同源:直接复用 novel-parse 的检查器。
77
+ import { checkProject } from './lib/novel-parse.mjs'
75
78
  import { loadYamlLib, resolveDshHome } from './lib/yaml-loader.mjs'
76
79
 
77
80
  // loadYamlLib / resolveDshHome 已迁至 lib/yaml-loader.mjs(创作台业务层与
@@ -937,7 +940,7 @@ export async function registerPrefixRouteWithRetry(webServer, route, { attempts
937
940
  }
938
941
 
939
942
  /**
940
- * 注册创作台全部路由(docs/creator-panel.md §2 的 11 条)。返回宿主 disposer;
943
+ * 注册创作台全部路由(docs/creator-panel.md §2 的 13 条)。返回宿主 disposer;
941
944
  * webServer 缺席(非 web 部署)时抛错,由 apply 侧统一降级为 warn。
942
945
  */
943
946
  export async function registerCreatorRoutes(ctx) {
@@ -990,6 +993,23 @@ export async function registerCreatorRoutes(ctx) {
990
993
  return
991
994
  }
992
995
 
996
+ if (route === 'self-update' && req.method === 'POST') {
997
+ // 面板内一键升级:形态/pnpm 检查 + `pnpm --dir <profileDir> add
998
+ // dsh-maoying@latest`(细节与安全边界见 lib/self-update.mjs)。可
999
+ // 预期的失败(dev-link / unsupported-layout / pnpm-missing / 超时 /
1000
+ // pnpm 非零退出)统一 HTTP 200 + ok:false,kind 给客户端分流、
1001
+ // message 经 sanitizeMessage 脱敏后透出;成功也带 restartRequired:
1002
+ // true——装完的只是磁盘上的新包,运行中的进程仍是旧代码,必须重启
1003
+ // DeepSeek 才生效,客户端文案必须照实转述。
1004
+ const r = await selfUpdate({ logger: ctx.logger })
1005
+ if (r.ok) {
1006
+ json(res, 200, { ok: true, from: r.from ?? null, to: r.to ?? null, restartRequired: true })
1007
+ } else {
1008
+ json(res, 200, { ok: false, kind: r.kind, error: sanitizeMessage(r.message ?? r.kind) })
1009
+ }
1010
+ return
1011
+ }
1012
+
993
1013
  if (route === 'host-debug' && req.method === 'GET') {
994
1014
  // 现场诊断:宿主版本五级探针轨迹 + 形状判定。与 client.css 同级信任
995
1015
  // 围栏(loopback + 可信 Host 才放行),只暴露版本与路径类信息。
@@ -1057,6 +1077,17 @@ export async function registerCreatorRoutes(ctx) {
1057
1077
  return
1058
1078
  }
1059
1079
 
1080
+ if (route === 'check' && req.method === 'GET') {
1081
+ // 静态体检:checkProject 读章节 frontmatter 原文——快照会把越枚举
1082
+ // status 归一成 pending(人工改坏在面板里看不出来),这里原样透出
1083
+ // 问题清单,概览「静态体检」黄条取数。checkProject 自带的 ok 是
1084
+ // 「无 error」语义、summary 是 CLI 退出码原料,都不进面板契约
1085
+ // (§2 出参就是 { ok, problems }),客户端自己按 level 计数。
1086
+ const novelDir = resolveNovelRoot(url.searchParams.get('path'), ctx.workspaceRegistry)
1087
+ json(res, 200, { ok: true, problems: checkProject(novelDir).problems })
1088
+ return
1089
+ }
1090
+
1060
1091
  if (route === 'chapter' && req.method === 'GET') {
1061
1092
  // 章节正文全文(抽屉用):file 只认 chapters/ 下纯文件名;响应组装
1062
1093
  // 直传 readChapter 结果(五期起含对应章纲 outline 字段,无章纲为
@@ -1090,7 +1121,9 @@ export async function registerCreatorRoutes(ctx) {
1090
1121
  if (route === 'project-save' && req.method === 'POST') {
1091
1122
  const body = await readBody(req, res)
1092
1123
  const novelDir = resolveNovelRoot(body.path, ctx.workspaceRegistry)
1093
- json(res, 200, mutateProject(novelDir, body.values))
1124
+ // base_hash = 面板表单载入时的 project.yml 指纹:外部改过 → 409 拒写
1125
+ // (人工改动保护);旧客户端不带该字段 → undefined,跳过检查。
1126
+ json(res, 200, mutateProject(novelDir, body.values, { baseHash: body.base_hash }))
1094
1127
  return
1095
1128
  }
1096
1129
 
package/lib/base.css CHANGED
@@ -294,6 +294,13 @@ html[data-dsh-maoying-active]:not([data-dsh-mnemon-active]):not([data-dsh-taskbo
294
294
  .myt-stripIcon { display: inline-flex; flex: none; }
295
295
  .myt-stripText { min-width: 0; overflow-wrap: anywhere; }
296
296
  .myt-stripWarn { border-color: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent); background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 10%, transparent); color: var(--dsw-alias-state-warn-primary);}
297
+ /* 静态体检黄条的错误强调(叠加在 myt-stripWarn 上):含 error 级问题时整条
298
+ 转错误色——纯警告(0 错误)保持黄色不动,一眼分清「有错」和「仅提醒」。 */
299
+ .myt-stripCheckErr {
300
+ border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);
301
+ background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);
302
+ color: var(--dsw-alias-state-error-primary);
303
+ }
297
304
  .myt-stripError {
298
305
  border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);
299
306
  background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);
@@ -705,6 +712,24 @@ html[data-dsh-maoying-active]:not([data-dsh-mnemon-active]):not([data-dsh-taskbo
705
712
  }
706
713
  .myt-verBadge:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 22%, transparent); }
707
714
 
715
+ /* 一键升级按钮:与版本徽章同形(描边胶囊),装完/安装中禁用降透明度 */
716
+ .myt-verUpgrade {
717
+ margin-left: 6px;
718
+ padding: 2px 10px;
719
+ border: 1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 45%, transparent);
720
+ border-radius: 999px;
721
+ background: transparent;
722
+ color: var(--dsw-alias-state-business-primary);
723
+ font-size: 11px;
724
+ line-height: 16px;
725
+ font-weight: 500;
726
+ vertical-align: middle;
727
+ white-space: nowrap;
728
+ cursor: pointer;
729
+ }
730
+ .myt-verUpgrade:hover:not(:disabled) { background: color-mix(in srgb, var(--dsw-alias-state-business-primary) 12%, transparent); }
731
+ .myt-verUpgrade:disabled { opacity: 0.6; cursor: default; }
732
+
708
733
  /* ── 版本号 + 系统设置(字号五档,面板整体 zoom)────────────────────────── */
709
734
  .myt-verTag {
710
735
  display: inline-block;