@chengzhao-dev/dsh-quarto-book 0.4.0 → 0.4.1

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
@@ -4,7 +4,7 @@
4
4
 
5
5
  **English** | [简体中文](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/README.md)
6
6
 
7
- Author and check Quarto Book chapters and maintain a reusable theme; no business code, only inside `type: book` repositories.
7
+ Write Quarto Book chapters, check their structure, and maintain the theme; only inside `type: book` repositories.
8
8
 
9
9
  - Write `.qmd`: `writing-quarto` covers the chapter skeleton, structure and prose.
10
10
  - Change the theme: `designing-theme` covers CSS, fonts and palettes.
@@ -19,171 +19,33 @@ Author and check Quarto Book chapters and maintain a reusable theme; no business
19
19
  | `quarto_render_check` | Check that `output-dir` is safe, render once when asked, and inspect HTML in `_book/` for publishing | Editing `.qmd`, fixing config automatically, or re-running a failed render |
20
20
  | `quarto_pass_report` | Compress the run into a fixed-line receipt card: skill, mode, files and steps changed, items held, check verdicts | Saving to disk, quoting chapter prose, or changing the text based on the receipt |
21
21
 
22
- For when the skills appear and which rule each step maps to, see [`docs/plugin-flow.md`](docs/plugin-flow.md). It is a human-facing walkthrough, not the rule source used while a skill runs.
22
+ For when the skills appear and which rule each step maps to, see [docs/plugin-flow.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-flow.md). It is a human-facing walkthrough, not the rule source used while a skill runs.
23
23
 
24
24
  Both skills are discovered from the package's `skills/` directory and loaded on demand. Relative paths inside a skill resolve against that skill's directory: `writing-quarto` uses `steps/`, `examples/`, `quarto/`, `scripts/`; `designing-theme` uses `references/`, `knowledge/`, `assets/`, `scripts/`.
25
25
 
26
26
  ## Install
27
27
 
28
28
  ```bash
29
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.4.0
29
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.4.1
30
30
  ```
31
31
 
32
- Replace `--profile` with the profile you actually run. The desktop app always runs the `desktop` profile, and that profile is owned by the app, so `dsh --profile desktop` is rejected; install or upgrade a plugin with `dsh plugin --profile desktop add <package>`, and use the app's Settings → Plugins → Add plugin with `npm:@chengzhao-dev/dsh-quarto-book@0.4.0` only if the app refuses that write. To follow a GitHub tag instead, use `github:chengzhao-dev/dsh-quarto-book#v0.4.0`.
32
+ Replace `--profile` with the profile you actually run. The desktop app always runs the `desktop` profile, and that profile is owned by the app, so `dsh --profile desktop` is rejected; install or upgrade a plugin with `dsh plugin --profile desktop add <package>`, and use the app's Settings → Plugins → Add plugin with `npm:@chengzhao-dev/dsh-quarto-book@0.4.1` only if the app refuses that write. To follow a GitHub tag instead, use `github:chengzhao-dev/dsh-quarto-book#v0.4.1`.
33
33
 
34
34
  Upgrading from the old unscoped package `dsh-quarto-book` (removed from npm with 0.3.4, so this only affects profiles that installed it earlier): remove it from the same profile first with `dsh plugin --profile <profile> remove dsh-quarto-book`, then add the scoped package. Keeping both installed registers `writing-quarto` and `designing-theme` twice at the same rank 550.
35
35
 
36
- Requires DSH 0.2.x and Node.js 22 or newer. The source is TypeScript; the published package already contains compiled `lib/`, so installation needs no build step.
36
+ Requires DSH 0.2.x and Node.js 22 or newer. The source is TypeScript; the published package already contains compiled `lib/`, so installation needs no build step. The content, link and naming checks need Python 3; `quarto_render_check` needs the Quarto CLI for an actual render, and the post-render HTML checks also need Python 3. See [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) for the split.
37
37
 
38
- Installation is scoped to a profile, not to a repository: it only affects the profile that ran `add`. The workspace gate below still decides whether the two skill names appear in a session.
38
+ Installation is scoped to a profile, not to a repository: it only affects the profile that ran `add`. The workspace gate still decides whether the two skill names appear: it checks the session `cwd` for a book, and only then are both names selectable. See [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) for the six cases.
39
39
 
40
- ## Workspace gate
40
+ ## Usage
41
41
 
42
- `apply()` registers the skill provider and the `quarto_render_check` tool; it writes nothing. The provider's `list({ cwd })` performs one bounded read-only check:
42
+ Load `writing-quarto` or `designing-theme` inside a `type: book` repository and follow the steps in `SKILL.md`. The plugin has no resident process and does not execute a skill when a session starts; seeing a name in the skill catalog only means the session `cwd` passed the gate, and a use happens only when `skill`, `quarto_render_check` or `quarto_pass_report` is invoked. The next invocation rereads the skill text; the tool never retries automatically, so the user decides whether to call it again after a failure.
43
43
 
44
- - Walk up from the session `cwd` to the enclosing git root. A `_quarto.yml` with a standalone `type: book` line plus at least one `.qmd` or `.md` chapter source returns both skills; otherwise the list is empty. Repository metadata such as `README.md`, `CHANGELOG.md` or `LICENSE.md` at the book root does not count as a chapter.
45
- - No `cwd`, no enclosing git checkout, a missing `_quarto.yml`, only a commented `# type: book`, or a book config with no chapter sources all count as "not a book".
46
- - The scan skips `_book`, `node_modules`, `vendor`, `third_party` and similar generated/dependency trees, and stops after 4000 directory entries. A normal C++ repository and this plugin's own checkout therefore never see these names; a subdirectory of a book repository behaves like the book root.
47
- - The gate affects discovery and loading. `get()` re-runs the gate when a `cwd` is supplied; without one it serves this provider's already-loaded candidates only.
48
- - `quarto_render_check` shares the gate: no session `cwd`, a non-book workspace, a `_quarto.yml` without `type: book`, or a book without chapter sources is refused before `quarto render` starts. The render root is the nearest valid book root above `cwd`; `qmd_path` only locates the card relative to the session `cwd`.
44
+ A project supplies exactly one skill of its own: `<book>/.agents/skills/quarto-book-project/`, starting from `templates/quarto-book-project/`. When it exists, both general skills read `SKILL.md` and then the `writing/` files it points to; when it does not, they skip it and do not guess project conventions. **Do not embed same-name skills into a book repository**: DSH picks one skill per name per layer by rank, lowest wins, and `.agents/skills` (rank 200) outranks the packaged provider (rank 550); a copy of `writing-quarto` or `designing-theme` in the book disables the plugin's text.
49
45
 
50
- Community plugins have no "enable for one GitHub repository" switch; profile installation plus the cwd gate is the smallest scope available. For a narrower scope, install the plugin only into the profile used for writing. See [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) for the full decision matrix, coexistence rules with `dsh-cpp-workflow`, and verification records.
46
+ Theme assets live in `skills/designing-theme/assets/theme/`. The package copy is authoritative; the book's `theme/` is the render copy. When a copy exists, `designing-theme` edits only that copy, and only when the user explicitly asks to install the plugin theme into the book and the target directory does not exist yet does the skill copy from the package and write the `filters/answering-disclosure.lua` filter. `_quarto.yml` references only the book's copy, never an npm package or the plugin checkout.
51
47
 
52
- ## Run state and completion
53
-
54
- The plugin has no resident process and does not execute a skill when a session starts.
55
-
56
- - **Whether it is active**: seeing `writing-quarto` or `designing-theme` in the skill catalog only means the session `cwd` passed the gate and the name is selectable. Absence is not an install failure; the current directory is not a book.
57
- - **Whether it is used**: a use happens only when the model or the user invokes `skill`, or calls `quarto_render_check`. Being listed reads and writes nothing.
58
- - **When it ends**: a skill has no background task; the turn that invokes it ends after the `SKILL.md` steps finish or stop. The render tool ends once `render_result` and `html_report` in the returned result are settled (both are `null` on a dry run); its card summary is one of `未渲染` (not rendered), `可渲染` (renderable), `渲染失败` (render failed) or `渲染完成` (render completed).
59
- - **Final verdict**: `html_report.usable` is the final render-check verdict. Without `html_report`, `safe_to_render` only means rendering is allowed, not that it happened.
60
- - **Using it again**: the next invocation rereads the skill text; the tool never retries automatically, so the user decides whether to call it again after a failure.
61
-
62
- ## Project-level conventions
63
-
64
- The general skills cannot know a particular book's title, chapter table, stack or terminology. A project supplies exactly one skill of its own:
65
-
66
- ```text
67
- <book>/.agents/skills/quarto-book-project/
68
- ├── SKILL.md # project conventions
69
- └── writing/ #/ project files
70
- └── chapter-map.md # chapter table
71
- ```
72
-
73
- Start from `templates/quarto-book-project/`. When the file exists, both general skills read `SKILL.md` and then the `writing/` files it points to; when it does not, they skip it and do not guess project conventions. General writing, structure and theme rules stay in the plugin and are not copied into the project skill.
74
-
75
- **Do not embed same-name skills into a book repository.** DSH picks one skill per name per layer by rank, lowest wins: `.agents/skills` (rank 200) outranks the packaged provider (rank 550). A copy of `writing-quarto` or `designing-theme` in the book disables the plugin's text and brings a second set of general rules into the project.
76
-
77
- ## Render check tool
78
-
79
- `quarto_render_check` takes two parameters:
80
-
81
- - `qmd_path`: the `.qmd` being edited, used to locate the card.
82
- - `dry_run`: `true` only judges the output directory; `false` runs `quarto render` once.
83
-
84
- Calls are bounded. `dry_run: true` is allowed at most twice: if the first call returns `safe_to_render: false`, change `output-dir` once; if the second call is still unsafe, stop and report `warnings`. `dry_run: false` is allowed once: on failure, stop and report `errors`; do not retry automatically. On success the same response carries `html_report`, so the same file is not rendered again.
85
-
86
- Returned fields:
87
-
88
- | Field | Meaning |
89
- | --- | --- |
90
- | `warnings` | Unsafe output directory, missing entry file, and similar notes |
91
- | `errors` | One-line reason for a failed render, timeout or missing `quarto`; empty on success |
92
- | `safe_to_render` | Whether rendering may run |
93
- | `suggested_output_dir` | Always `_book` |
94
- | `render_result` | `ok`, `exit_code`, `duration_ms` and captured tail; `null` when `dry_run: true` |
95
- | `html_report` | `usable`, `checks`, `output_files`, `output_files_total`; `null` when `dry_run: true` |
96
-
97
- Each `html_report.checks` item is `{ id, status, detail }`, with `status` limited to `pass`, `fail` and `skip`. Fixed checks are `index_html`, `utf8`, `charset` and `no_file_uri`; conditional checks are `sitemap`, `search_index`, `callouts` and `dom`. `usable` is true when no check fails. `output_files` lists at most 30 HTML files; `output_files_total` gives the full count.
98
-
99
- The interception, correction and success path looks like this. Start with a wrong output directory:
100
-
101
- ```yaml
102
- project:
103
- type: book
104
- output-dir: ./
105
- ```
106
-
107
- Call `quarto_render_check(qmd_path: "content/chapter.qmd", dry_run: true)`. It returns `safe_to_render: false`; `warnings` says `./` would overwrite the book root and suggests `output-dir: _book`. Change that line to `output-dir: _book` and call again: `safe_to_render: true`, `render_result: null`. Call with `dry_run: false` and the same response contains:
108
-
109
- ```json
110
- {
111
- "errors": [],
112
- "render_result": { "ok": true, "exit_code": 0 },
113
- "html_report": {
114
- "usable": true,
115
- "checks": [
116
- { "id": "index_html", "status": "pass" },
117
- { "id": "charset", "status": "pass" },
118
- { "id": "sitemap", "status": "pass" },
119
- { "id": "search_index", "status": "pass" }
120
- ]
121
- }
122
- }
123
- ```
124
-
125
- The raw render output never becomes a separate terminal card. The render timeout is 120 seconds; timeouts, a missing `quarto` executable and non-zero exits all go into `errors`.
126
-
127
- ## Run receipt
128
-
129
- At the end of a run the skill calls `quarto_pass_report` once, compressing the run into a few fixed lines. The card is the receipt; the reply does not repeat it, and the tool saves nothing to disk, quotes no chapter prose, and changes no text based on the receipt.
130
-
131
- Every parameter is a closed set:
132
-
133
- | Field | Values |
134
- | --- | --- |
135
- | `skill` | `writing-quarto` or `designing-theme` |
136
- | `executed` | Boolean; when `false`, `mode` must be `skipped` and `skip_reason` (at most 40 characters) is required |
137
- | `mode` | `polish`, `rewrite` (writing), `theme` (theme), `skipped` (not executed) |
138
- | `files` | Up to 8 `{ path, edits }` entries |
139
- | `steps` | Only steps with edits: `1`–`9` for writing, or `定表面`, `定字体`, `定尺度`, `定颜色`, `定组件`, `通过检查` for the theme; their `edits` must sum to the `files` total |
140
- | `held` | `fences`, `facts`, `css`, `prose` |
141
- | `checks` | `content`, `html`, `layout`, with status `pass`, `fail` or `skip` |
142
-
143
- ```text
144
- 已执行 writing-quarto · 增量润色
145
- content/ch.qmd
146
- 改了 5 处:定句子 3,定用词 1,定版式 1
147
- 未改:围栏、事实
148
- 检查:内容通过;HTML 可用
149
- ```
150
-
151
- A zero-change run still prints a card (`改了 0 处`), so "the plugin never ran" stays distinguishable from "it ran and changed nothing"; when the current file is not a chapter the output is one line: `未执行 writing-quarto:…`.
152
-
153
- ## Theme
154
-
155
- Theme assets live in `skills/designing-theme/assets/theme/`:
156
-
157
- ```text
158
- assets/theme/
159
- ├── assets/ #/ brand mark, favicon, fonts and OFL licence
160
- ├── css/ #/ base, content, nav, sidebar, callouts, code, mermaid…
161
- ├── includes/ #/ font and footer snippets
162
- └── palettes/github/ #/ light/dark SCSS, highlight themes, tokens.css
163
- ```
164
-
165
- Quarto reads relative paths inside the book; it reads neither npm packages nor DSH profiles. A book that renders uses its own `theme/` copy, and `_quarto.yml` references only that copy:
166
-
167
- ```yaml
168
- format:
169
- html:
170
- theme:
171
- light: theme/palettes/github/theme-light.scss
172
- dark: theme/palettes/github/theme-dark.scss
173
- css:
174
- - theme/css/tokens.css
175
- - theme/css/base.css
176
- include-in-header:
177
- - theme/includes/fonts.html
178
- include-after-body:
179
- - theme/includes/footer.html
180
- ```
181
-
182
- The package's `assets/theme/` is the authoritative source; the book's `theme/` is the render copy. When a copy exists, `designing-theme` edits only that copy. Only when the user explicitly asks to install the plugin theme into the book and the target directory does not exist yet does the skill copy from the package, also writing the `filters/answering-disclosure.lua` filter; an existing `_quarto.yml` only gains missing paths and is never replaced wholesale.
183
-
184
- ## Check scripts
185
-
186
- `writing-quarto` ships content and link checks under its `scripts/` directory. Run them from that skill directory (installed at `skills/writing-quarto/`):
48
+ Run the content and link checks from the `writing-quarto` skill directory:
187
49
 
188
50
  ```bash
189
51
  python scripts/verifying-content.py --root <book>
@@ -191,51 +53,21 @@ python scripts/checking-links.py --root <book>
191
53
  python scripts/checking-names.py --root <book>
192
54
  ```
193
55
 
194
- `verifying-content.py` checks heading depth and length, body size budgets, forbidden wording, code fences, fragment sources and chapter-table consistency. Project-specific checks run only when `.agents/skills/quarto-book-project/` exists; otherwise they are skipped so another book's chapter names never become rules.
195
-
196
- Callouts, HTML output and DOM contracts are checked by `quarto_render_check`, which invokes the packaged scripts. Do not run `checking-callouts.py`, `checking-output.py` or `checking-dom.py` by hand. `designing-theme` ships `scripts/checking-layout.py`, which inspects rendered HTML for overflow, contrast and page structure, and `scripts/measuring-pages.mjs` for measuring the content column.
197
-
198
- `apply()` runs none of these and never runs `quarto render`. Verification is triggered by the completion criteria in the skill text, after the user asks for a chapter or a theme change.
199
-
200
- ## Package layout
201
-
202
- ```text
203
- dsh-quarto-book/
204
- ├── .github/workflows/publish.yml # tag release
205
- ├── .gitattributes # line endings
206
- ├── .gitignore # ignored output
207
- ├── .npmignore # excluded files
208
- ├── LICENSE # MIT licence
209
- ├── README.en.md # English guide
210
- ├── README.md # Chinese guide
211
- ├── cordis.patch.yml # host patch
212
- ├── docs #/ design notes
213
- ├── icon.svg # plugin icon
214
- ├── lib #/ compiled output
215
- │ ├── index.js # registration entry
216
- │ ├── tools/render-check.js # render check tool
217
- │ └── workspace.js # workspace gate
218
- ├── locale #/ UI strings
219
- ├── package-lock.json # dependency lock
220
- ├── package.json # package manifest
221
- ├── scripts #/ repository scripts
222
- │ ├── checking-skill-links.mjs # skill self-check
223
- │ └── embedding-agents.ps1 # legacy embed script
224
- ├── skills #/ both skills
225
- │ ├── designing-theme #/ theme skill
226
- │ └── writing-quarto #/ writing skill
227
- ├── src #/ TypeScript sources
228
- │ ├── index.ts # plugin entry
229
- │ ├── tools/render-check.ts # render check implementation
230
- │ └── workspace.ts # workspace gate
231
- ├── templates #/ project skill template
232
- ├── tests #/ tool unit tests
233
- │ ├── provider.test.mjs # skill gate tests
234
- │ └── render-check.test.mjs # render tool tests
235
- └── tsconfig.json # TypeScript config
236
- ```
56
+ Add `checking-names.py` only for a whole-repository refactor; ordinary chapter checks run the first two.
57
+
58
+ Callouts, HTML output and DOM contracts after a render are checked by `quarto_render_check`, which invokes the packaged scripts; do not run `checking-callouts.py`, `checking-output.py` or `checking-dom.py` by hand. `designing-theme`'s `scripts/checking-layout.py` inspects rendered HTML for overflow, contrast and page structure. `apply()` runs none of these and never runs `quarto render`.
59
+
60
+ At the end of a run, call `quarto_pass_report` once to print the receipt card; the card is the receipt, so do not repeat it in the reply.
61
+
62
+ ## Documentation
237
63
 
238
- `templates/` is outside `skills/` and is never registered. The skill layout is described in `docs/writing-quarto-layout.md`; versioning and release rules in `docs/versioning.md`.
64
+ | Document | Contents |
65
+ | --- | --- |
66
+ | [docs/plugin-flow.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-flow.md) | The five layers from install to receipt, file routing and render order |
67
+ | [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) | The six workspace-gate cases and their decision order |
68
+ | [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) | Repository tree, published files and the Python/Quarto split |
69
+ | [docs/writing-quarto-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/writing-quarto-layout.md) | The writing skill's directory layers |
70
+ | [docs/versioning.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/versioning.md) | Version numbers and release order |
239
71
 
240
72
  ## Local development
241
73
 
@@ -245,10 +77,10 @@ The source is TypeScript and the compiled `lib/` is committed. Published package
245
77
  npm ci
246
78
  npm test
247
79
  npm run check
248
- npm run pack:check
80
+ npm run pack
249
81
  ```
250
82
 
251
- `npm test` builds before running the tool tests; `npm run check` runs type checking and the skill path self-check. Development install:
83
+ `npm test` builds before running the tool tests; `npm run check` runs type checking and the skill path self-check; `npm run pack` writes the tarball only into `temp/`, which `.gitignore` already covers. Development install:
252
84
 
253
85
  ```bash
254
86
  dsh plugin --profile web add D:\Github\dsh-quarto-book
@@ -258,17 +90,7 @@ dsh plugin --profile web add D:\Github\dsh-quarto-book
258
90
 
259
91
  The plugin ships as a bundle: `package.json` points `dsh.bundle.patch` at `cordis.patch.yml`, and the patch inserts only `id: quarto-book` / `name: '@chengzhao-dev/dsh-quarto-book'`. The entry imports nothing from the host, so it loads under pnpm's strict dependency layout.
260
92
 
261
- ## Embedding into another book (not recommended)
262
-
263
- `scripts/embedding-agents.ps1` is a maintainer tool from the older layout. It copies both skill trees verbatim into the target repository's `.agents/`. Those copies use the same names as the packaged skills and outrank them (rank 200 vs 550), so plugin updates stop taking effect.
264
-
265
- Do not use it for new books: keep only `.agents/skills/quarto-book-project/` in the book and let the installed plugin supply the rest. Run it only to archive a standalone skill tree offline:
266
-
267
- ```powershell
268
- pwsh -File scripts/embedding-agents.ps1 -Target D:\Github\my-book
269
- ```
270
-
271
- The script refuses to copy `package.json`, `cordis.patch.yml`, `lib/`, `locale/` or `icon.svg`, so bundle manifests never leak into a book repository.
93
+ The legacy `scripts/embedding-agents.ps1` copies both skill trees verbatim into the target repository's `.agents/`, where they outrank the plugin (rank 200 vs 550). Do not use it for new books; run it only to archive a standalone skill tree offline, with the command from the script header.
272
94
 
273
95
  ## Licence
274
96
 
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [English](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/README.en.md) | **简体中文**
6
6
 
7
- 为 Quarto Book 写章节、校验结构并维护可复用主题;不改业务代码,只在 `type: book` 的仓库里出现。
7
+ 为 Quarto Book 写章节、检查结构并维护主题;只在 `type: book` 的仓库里出现。
8
8
 
9
9
  - 写 `.qmd`:`writing-quarto` 管章节骨架、结构与行文。
10
10
  - 改主题:`designing-theme` 管 CSS、字体与 palette。
@@ -19,171 +19,33 @@
19
19
  | `quarto_render_check` | 先判断 `output-dir` 是否安全,再按需渲染一次,检查 `_book/` 里的 HTML 是否可发布 | 不改 `.qmd`,不自动修配置,不重复渲染 |
20
20
  | `quarto_pass_report` | 收尾时把执行结论压成一张固定行数的回执卡片:技能、模式、改过的文件与步骤、未改项、检查结论 | 不保存到磁盘,不贴章节原文,不据回执改文章 |
21
21
 
22
- 装上之后什么时候出现、每一步对应哪条规则,见 [`docs/plugin-flow.md`](docs/plugin-flow.md)。那是给人回看的流程说明,不是技能执行时的规则来源。
22
+ 装上之后什么时候出现、每一步对应哪条规则,见 [docs/plugin-flow.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-flow.md)。那是给人回看的流程说明,不是技能执行时的规则来源。
23
23
 
24
24
  两个技能从包内 `skills/` 目录发现,按需加载;技能里的相对路径以技能目录为基准解析:`writing-quarto` 用 `steps/`、`examples/`、`quarto/`、`scripts/`,`designing-theme` 用 `references/`、`knowledge/`、`assets/`、`scripts/`。
25
25
 
26
26
  ## 安装
27
27
 
28
28
  ```bash
29
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.4.0
29
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@0.4.1
30
30
  ```
31
31
 
32
- `--profile` 换成你实际运行的那个 profile。桌面应用固定跑 `desktop` profile,且该 profile 由应用独占管理,`dsh --profile desktop` 直接启动会被拒绝;安装或升级插件可用 `dsh plugin --profile desktop add <包>`,如果应用拒绝写入,再走「设置 → 插件 → 添加插件」,地址填 `npm:@chengzhao-dev/dsh-quarto-book@0.4.0`。想跟 GitHub 标签,也可以写 `github:chengzhao-dev/dsh-quarto-book#v0.4.0`。
32
+ `--profile` 换成你实际运行的那个 profile。桌面应用固定跑 `desktop` profile,且该 profile 由应用独占管理,`dsh --profile desktop` 直接启动会被拒绝;安装或升级插件可用 `dsh plugin --profile desktop add <包>`,如果应用拒绝写入,再走「设置 → 插件 → 添加插件」,地址填 `npm:@chengzhao-dev/dsh-quarto-book@0.4.1`。想跟 GitHub 标签,也可以写 `github:chengzhao-dev/dsh-quarto-book#v0.4.1`。
33
33
 
34
34
  从旧的无作用域包 `dsh-quarto-book` 升级时(该包已随 0.3.4 从 npm 删除,只影响之前装过它的 profile),先在同一个 profile 里 `dsh plugin --profile <profile> remove dsh-quarto-book` 再装新包。两个包同时留着会以相同的 rank 550 各注册一遍 `writing-quarto` 和 `designing-theme`。
35
35
 
36
- 要求 DSH 0.2.x、Node.js 22 及以上。源码是 TypeScript,发布包已带编译后的 `lib/`;安装后不需要构建。
36
+ 要求 DSH 0.2.x、Node.js 22 及以上。源码是 TypeScript,发布包已带编译后的 `lib/`,安装后不需要构建。内容、链接与命名检查需要 Python 3;`quarto_render_check` 真正渲染时需要 Quarto CLI,渲染后的 HTML 检查同样用 Python 3。环境分工见 [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md)。
37
37
 
38
- 安装单位是 profile,不是仓库:只影响执行 `add` 的那个 profile。装好之后还要过下面的工作区门禁,目录里才会出现这两个技能名。
38
+ 安装单位是 profile,不是仓库:只影响执行 `add` 的那个 profile。装好之后还要过工作区门禁:按会话 `cwd` 判断当前目录是不是一本书,通过后两个技能名才会出现。门禁的六类情形见 [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md)。
39
39
 
40
- ## 工作区门禁
40
+ ## 使用
41
41
 
42
- `apply()` 注册技能 provider 和 `quarto_render_check` 工具,不写文件。provider 的 `list({ cwd })` 只在发现候选时做一次有界只读检查:
42
+ 在 `type: book` 的仓库里加载 `writing-quarto` 或 `designing-theme`,按 `SKILL.md` 的步骤执行。插件没有常驻进程,也不会在会话开始时自动执行技能;技能目录里出现名字只说明当前会话过了门禁,只有调用 `skill`、`quarto_render_check` 或 `quarto_pass_report` 才算一次使用。同一会话再次调用会重新读技能正文;工具不会自动重试,失败后由用户决定是否再调一次。
43
43
 
44
- - 从会话 `cwd` 向上找到 git 根,沿途目录的 `_quarto.yml` 里只要有独立的一行 `type: book`,并且这本书至少有一个 `.qmd` 或 `.md` 章节源文件,就返回这两个技能;否则返回空列表。书根下的 `README.md`、`CHANGELOG.md`、`LICENSE.md` 等仓库说明文件不算章节源文件。
45
- - 没有 `cwd`、不在 git checkout 里、`_quarto.yml` 缺失、只在注释里写了 `# type: book`、或只有 book 配置没有任何章节源文件,都按「不是书」处理。
46
- - 扫描跳过 `_book`、`node_modules`、`vendor`、`third_party` 等生成或依赖目录,最多 4000 个目录项;普通 C++ 仓库和本插件仓库自己的会话里不会出现这两个名字;书仓库的子目录会话与书根会话一样生效。
47
- - 门禁只影响技能发现与加载。`get()` 带 `cwd` 时重新过同一道门禁,不带 `cwd` 时只服务已经加载的本 provider candidate。
48
- - `quarto_render_check` 与技能共用同一道门禁:没有会话 `cwd`、不在书里、`_quarto.yml` 不是 `type: book`、或书里没有章节源文件时直接拒绝,不启动 `quarto render`。渲染根取 `cwd` 向上最近的合法书根,`qmd_path` 只按会话 `cwd` 定位卡片。
44
+ 项目只放一份自己的约定技能:`<book>/.agents/skills/quarto-book-project/`,模板见 `templates/quarto-book-project/`。存在时两个通用技能先读它的 `SKILL.md`,再按它指向的 `writing/` 文件读章节表;不存在就跳过,不猜项目约定。**不要把同名技能嵌进书仓库**:DSH 在同一技能层按 rank 取一个,`.agents/skills`(rank 200)优先于插件(rank 550),书里再放 `writing-quarto` 或 `designing-theme` 会让插件正文失效。
49
45
 
50
- 社区插件没有「只对某个 GitHub 仓库启用」的开关,profile 安装加 cwd 门禁就是最小范围。想更窄,就只把它装进写书用的那个 profile。完整判定矩阵、与 `dsh-cpp-workflow` 的共存规则和实际校验记录见 [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md)。
46
+ 主题资产在 `skills/designing-theme/assets/theme/`,包内是权威来源,书内 `theme/` 是渲染副本;已有副本时 `designing-theme` 只改副本,只有用户明确要求「把插件主题装进这本书」且目标目录不存在时才从包复制,并写入过滤器 `filters/answering-disclosure.lua`。`_quarto.yml` 只引用书内副本,不指向 npm 包或插件检出目录。
51
47
 
52
- ## 运行状态与结束
53
-
54
- 插件没有常驻进程,也不会在会话开始时自动执行技能。
55
-
56
- - **是否生效**:技能目录里出现 `writing-quarto` 或 `designing-theme`,只说明当前会话的 `cwd` 过了门禁,名字可选;没有出现不是安装失败,而是当前目录不是书。
57
- - **是否使用**:只有模型或用户调用 `skill`、或调用 `quarto_render_check`,才算一次使用;列出名字本身不读写任何文件。
58
- - **怎么结束**:技能没有后台任务,调用它的那一轮按 `SKILL.md` 的步骤完成或停下后就结束;渲染工具在返回结果里的 `render_result` 与 `html_report` 都确定后结束(dry_run 时两者都是 `null`),卡片摘要只取 `未渲染`、`可渲染`、`渲染失败`、`渲染完成` 之一。
59
- - **最终判断**:`html_report.usable` 是渲染检查的最终结论;没有 `html_report` 时,`safe_to_render` 只表示可以渲染,不表示已经渲染。
60
- - **再次使用**:同一会话再次调用会重新读技能正文;工具不会自动重试,失败后由用户决定是否再调一次。
61
-
62
- ## 项目级约定
63
-
64
- 通用技能不知道某一本书的书名、章节表、技术栈和术语。项目只放这一份自己的技能:
65
-
66
- ```text
67
- <book>/.agents/skills/quarto-book-project/
68
- ├── SKILL.md # 项目约定
69
- └── writing/ #/ 项目文件
70
- └── chapter-map.md # 章节表
71
- ```
72
-
73
- 模板见 `templates/quarto-book-project/`。存在时,两个通用技能会先读 `SKILL.md`,再按它指向的 `writing/` 文件读章节对照;不存在就跳过,不猜测项目约定。通用写法、结构和主题规则留在插件里,不复制进项目技能。
74
-
75
- **不要把同名技能嵌进书仓库。** DSH 在同一个技能层里按 rank 取一个,数字小的赢:`.agents/skills`(rank 200)优先于插件(rank 550)。书里再放 `writing-quarto` 或 `designing-theme`,插件正文就不会生效,还会把两套通用写法带进项目。
76
-
77
- ## 渲染检查工具
78
-
79
- `quarto_render_check` 有两个参数:
80
-
81
- - `qmd_path`:正在改的 `.qmd` 路径,用于卡片定位。
82
- - `dry_run`:`true` 只判断输出目录,`false` 才运行一次 `quarto render`。
83
-
84
- 调用次数有上限。`dry_run: true` 最多 2 次:第 1 次返回 `safe_to_render: false` 时只改一次 `output-dir`;第 2 次仍不安全就停住,把 `warnings` 交给用户。`dry_run: false` 最多 1 次:渲染失败就停住,把 `errors` 交给用户,不自动重跑。成功时同一次返回里带 `html_report`,不再为同一份文件重跑。
85
-
86
- 返回字段:
87
-
88
- | 字段 | 内容 |
89
- | --- | --- |
90
- | `warnings` | 输出目录不安全,或入口文件缺失等提示 |
91
- | `errors` | 渲染失败、超时或找不到 `quarto` 时的一句话原因;成功时为空 |
92
- | `safe_to_render` | 是否允许执行渲染 |
93
- | `suggested_output_dir` | 固定为 `_book` |
94
- | `render_result` | `ok`、`exit_code`、`duration_ms` 与末尾输出;`dry_run: true` 时为 `null` |
95
- | `html_report` | `usable`、`checks`、`output_files`、`output_files_total`;`dry_run: true` 时为 `null` |
96
-
97
- `html_report.checks` 每项是 `{ id, status, detail }`,`status` 只取 `pass`、`fail`、`skip`。固定项有 `index_html`、`utf8`、`charset`、`no_file_uri`;条件项有 `sitemap`、`search_index`、`callouts`、`dom`。`usable` 在没有 `fail` 时为 true。`output_files` 超过 30 个时只列前 30 个,总数在 `output_files_total`。
98
-
99
- 拦截、修正与成功各一次,演示如下。书的 `_quarto.yml` 先把输出目录写错:
100
-
101
- ```yaml
102
- project:
103
- type: book
104
- output-dir: ./
105
- ```
106
-
107
- 调用 `quarto_render_check(qmd_path: "content/chapter.qmd", dry_run: true)`,返回 `safe_to_render: false`,`warnings` 指出 `./` 会覆盖书根,并建议 `output-dir: _book`。把该行改成 `output-dir: _book` 后再查一次,返回 `safe_to_render: true`、`render_result: null`。最后调用 `dry_run: false`,同一次返回:
108
-
109
- ```json
110
- {
111
- "errors": [],
112
- "render_result": { "ok": true, "exit_code": 0 },
113
- "html_report": {
114
- "usable": true,
115
- "checks": [
116
- { "id": "index_html", "status": "pass" },
117
- { "id": "charset", "status": "pass" },
118
- { "id": "sitemap", "status": "pass" },
119
- { "id": "search_index", "status": "pass" }
120
- ]
121
- }
122
- }
123
- ```
124
-
125
- 渲染进程的原始输出不单独占一张终端卡片。命令超时是 120 秒;超时、找不到 `quarto` 或非零退出都写进 `errors`。
126
-
127
- ## 执行回执
128
-
129
- 技能收尾时调用一次 `quarto_pass_report`,把这次执行的结论压成固定几行。卡片就是回执,回复里不再复述;工具不保存到磁盘,不贴章节原文,也不据回执改文章。
130
-
131
- 入参是闭集:
132
-
133
- | 字段 | 取值 |
134
- | --- | --- |
135
- | `skill` | `writing-quarto` 或 `designing-theme` |
136
- | `executed` | 布尔;为 `false` 时 `mode` 必须是 `skipped`,并给 `skip_reason`(最多 40 字) |
137
- | `mode` | `polish`、`rewrite`(写作),`theme`(主题),`skipped`(未执行) |
138
- | `files` | 最多 8 个 `{ path, edits }` |
139
- | `steps` | 只列有改动的步骤:写作填 `1`–`9`,主题填 `定表面`、`定字体`、`定尺度`、`定颜色`、`定组件`、`通过检查`;`edits` 合计必须等于 `files` 的合计 |
140
- | `held` | `fences`、`facts`、`css`、`prose` |
141
- | `checks` | `content`、`html`、`layout`,状态只取 `pass`、`fail`、`skip` |
142
-
143
- ```text
144
- 已执行 writing-quarto · 增量润色
145
- content/ch.qmd
146
- 改了 5 处:定句子 3,定用词 1,定版式 1
147
- 未改:围栏、事实
148
- 检查:内容通过;HTML 可用
149
- ```
150
-
151
- 没改也会出卡(`改了 0 处`),这样「插件没跑」和「跑了但没改」分得开;当前文件不是章节时输出一行 `未执行 writing-quarto:…`。
152
-
153
- ## 主题
154
-
155
- 主题资产在 `skills/designing-theme/assets/theme/`:
156
-
157
- ```text
158
- assets/theme/
159
- ├── assets/ #/ brand mark、favicon、字体与 OFL 许可
160
- ├── css/ #/ base、content、nav、sidebar、callouts、code、mermaid…
161
- ├── includes/ #/ 字体与页脚片段
162
- └── palettes/github/ #/ light/dark SCSS、highlight theme、tokens.css
163
- ```
164
-
165
- Quarto 只读书里的相对路径,不读 npm 包,也不读 DSH profile。书要渲染时使用书根下的 `theme/` 副本,`_quarto.yml` 也只引用这份副本:
166
-
167
- ```yaml
168
- format:
169
- html:
170
- theme:
171
- light: theme/palettes/github/theme-light.scss
172
- dark: theme/palettes/github/theme-dark.scss
173
- css:
174
- - theme/css/tokens.css
175
- - theme/css/base.css
176
- include-in-header:
177
- - theme/includes/fonts.html
178
- include-after-body:
179
- - theme/includes/footer.html
180
- ```
181
-
182
- 插件包内 `assets/theme/` 是权威来源,书内 `theme/` 是渲染副本。已有副本时,`designing-theme` 只改副本。只有用户明确要求「把插件主题装进这本书」且目标目录还不存在时,才从插件复制过去,并同时写入过滤器 `filters/answering-disclosure.lua`;已存在的 `_quarto.yml` 只追加还没有的路径,不整文件替换。
183
-
184
- ## 校验脚本
185
-
186
- `writing-quarto` 自带内容与链接检查脚本。在 `writing-quarto` 技能目录下运行(安装后位于插件的 `skills/writing-quarto/`):
48
+ 内容与链接检查在 `writing-quarto` 技能目录下运行:
187
49
 
188
50
  ```bash
189
51
  python scripts/verifying-content.py --root <book>
@@ -191,51 +53,21 @@ python scripts/checking-links.py --root <book>
191
53
  python scripts/checking-names.py --root <book>
192
54
  ```
193
55
 
194
- `verifying-content.py` 检查标题层级与字数、正文篇幅、禁用措辞、代码围栏、本地链接和章节表一致性。项目专属检查只在 `.agents/skills/quarto-book-project/` 存在时启用;不存在时跳过,不把别的书的章节名当成规则。
195
-
196
- 渲染后的 Callout、HTML 产物与 DOM 契约由 `quarto_render_check` 调用包内脚本完成;不要再手跑 `checking-callouts.py`、`checking-output.py` 或 `checking-dom.py`。`designing-theme` 自带 `scripts/checking-layout.py`,在渲染后的 HTML 上检查溢出、对比度和页面结构;`scripts/measuring-pages.mjs` 用于测量正文列宽。
197
-
198
- `apply()` 不在加载时跑这些脚本,也不跑 `quarto render`。校验由技能正文的完成判据触发,发生在用户要求写章节或改主题之后。
199
-
200
- ## 包结构
201
-
202
- ```text
203
- dsh-quarto-book/
204
- ├── .github/workflows/publish.yml # 标签发布
205
- ├── .gitattributes # 行尾与编码
206
- ├── .gitignore # 忽略的构建产物
207
- ├── .npmignore # 排除的文件
208
- ├── LICENSE # MIT 许可
209
- ├── README.en.md # 英文说明
210
- ├── README.md # 中文说明
211
- ├── cordis.patch.yml # 宿主补丁
212
- ├── docs #/ 设计说明
213
- ├── icon.svg # 插件图标
214
- ├── lib #/ 编译产物
215
- │ ├── index.js # 注册入口
216
- │ ├── tools/render-check.js # 渲染检查工具
217
- │ └── workspace.js # 工作区门禁
218
- ├── locale #/ 界面文案
219
- ├── package-lock.json # 依赖锁
220
- ├── package.json # 包清单
221
- ├── scripts #/ 仓库脚本
222
- │ ├── checking-skill-links.mjs # 技能自检
223
- │ └── embedding-agents.ps1 # 旧装机脚本
224
- ├── skills #/ 两个技能
225
- │ ├── designing-theme #/ 主题技能
226
- │ └── writing-quarto #/ 写作技能
227
- ├── src #/ TypeScript 源码
228
- │ ├── index.ts # 插件入口
229
- │ ├── tools/render-check.ts # 渲染检查实现
230
- │ └── workspace.ts # 工作区门禁
231
- ├── templates #/ 项目技能模板
232
- ├── tests #/ 工具单元测试
233
- │ ├── provider.test.mjs # 技能门禁测试
234
- │ └── render-check.test.mjs # 渲染工具测试
235
- └── tsconfig.json # TypeScript 配置
236
- ```
56
+ `checking-names.py` 只在全仓重构时加跑;日常章节检查跑前两条即可。
57
+
58
+ 渲染后的 Callout、HTML 产物与 DOM 契约由 `quarto_render_check` 调包内脚本完成,不手跑 `checking-callouts.py`、`checking-output.py` 或 `checking-dom.py`;`designing-theme` 的 `scripts/checking-layout.py` 在渲染后的 HTML 上检查溢出、对比度和页面结构。`apply()` 不在加载时跑这些脚本,也不跑 `quarto render`。
59
+
60
+ 一轮结束时调用一次 `quarto_pass_report`,把这次执行的结论压成卡片;卡片就是回执,回复里不再复述。
61
+
62
+ ## 文档
237
63
 
238
- `templates/` 不在 `skills/` 下,不会被注册。技能的目录分层见 `docs/writing-quarto-layout.md`,版本与发布规则见 `docs/versioning.md`。
64
+ | 文档 | 内容 |
65
+ | --- | --- |
66
+ | [docs/plugin-flow.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-flow.md) | 从安装到回执的五层结构、文件分流与渲染顺序 |
67
+ | [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) | 工作区门禁的六类情形与判定顺序 |
68
+ | [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) | 仓库目录、发布包内容与 Python/Quarto 分工 |
69
+ | [docs/writing-quarto-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/writing-quarto-layout.md) | 写作技能的目录分层 |
70
+ | [docs/versioning.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/versioning.md) | 版本号与发布顺序 |
239
71
 
240
72
  ## 本地开发
241
73
 
@@ -245,10 +77,10 @@ dsh-quarto-book/
245
77
  npm ci
246
78
  npm test
247
79
  npm run check
248
- npm run pack:check
80
+ npm run pack
249
81
  ```
250
82
 
251
- `npm test` 会先编译再跑工具测试;`npm run check` 做类型检查与技能路径自检。开发安装:
83
+ `npm test` 会先编译再跑工具测试;`npm run check` 做类型检查与技能路径自检;`npm run pack` 把 tarball 只写进 `temp/`,那个目录已在 `.gitignore` 里。开发安装:
252
84
 
253
85
  ```bash
254
86
  dsh plugin --profile web add D:\Github\dsh-quarto-book
@@ -258,17 +90,7 @@ dsh plugin --profile web add D:\Github\dsh-quarto-book
258
90
 
259
91
  插件以 bundle 形式发布:`package.json` 的 `dsh.bundle.patch` 指向 `cordis.patch.yml`,补丁只插入 `id: quarto-book`、`name: '@chengzhao-dev/dsh-quarto-book'` 一行。入口不 import 宿主实现,因此在 pnpm 的严格依赖布局下也能加载。
260
92
 
261
- ## 嵌入到另一本书(不推荐)
262
-
263
- `scripts/embedding-agents.ps1` 是旧形态的维护工具,会把两棵技能原样复制进目标仓库的 `.agents/`。复制后书里会出现与插件同名的技能,按 rank 200 盖住插件(rank 550),插件更新也不再生效。
264
-
265
- 新书不要用它:书里只放 `.agents/skills/quarto-book-project/`,其余交给已安装的插件。只有需要离线归档一份独立技能树时才执行:
266
-
267
- ```powershell
268
- pwsh -File scripts/embedding-agents.ps1 -Target D:\Github\my-book
269
- ```
270
-
271
- 脚本拒绝复制 `package.json`、`cordis.patch.yml`、`lib/`、`locale/` 和 `icon.svg`,避免把插件清单带进书仓库。
93
+ 旧装机脚本 `scripts/embedding-agents.ps1` 会把两棵技能原样复制进目标仓库的 `.agents/`,按 rank 200 盖住插件;新书不要用它,只有离线归档独立技能树时才执行,命令写在脚本头部。
272
94
 
273
95
  ## 许可
274
96