@chengzhao-dev/dsh-quarto-book 0.6.2 → 0.6.4
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 +60 -31
- package/README.md +53 -24
- package/lib/index.js +49 -7
- package/lib/tools/pass-report.js +12 -12
- package/lib/tools/render-check.js +231 -62
- package/lib/workspace.js +1 -0
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +13 -7
- package/skills/designing-theme/assets/theme/assets/fonts/lxgw-bright-code-common.woff2 +0 -0
- package/skills/designing-theme/assets/theme/assets/fonts/lxgw-bright-medium-common.woff2 +0 -0
- package/skills/designing-theme/assets/theme/css/content.css +15 -0
- package/skills/designing-theme/assets/theme/css/mermaid.css +5 -6
- package/skills/designing-theme/knowledge/reading-surface.md +1 -1
- package/skills/designing-theme/references/theme-system.md +5 -5
- package/skills/designing-theme/scripts/checking-layout.py +60 -12
- package/skills/designing-theme/scripts/measuring-pages.mjs +2 -2
- package/skills/writing-quarto/SKILL.md +7 -0
- package/skills/writing-quarto/examples/02-structure-shape.md +26 -12
- package/skills/writing-quarto/examples/03-block-after.md +14 -0
- package/skills/writing-quarto/examples/03-qmd-elements.md +45 -2
- package/skills/writing-quarto/examples/04-paragraph-cases.md +14 -0
- package/skills/writing-quarto/examples/04-paragraph-opening.md +2 -2
- package/skills/writing-quarto/examples/05-sentence-frame.md +4 -4
- package/skills/writing-quarto/examples/06-wording-cases.md +23 -0
- package/skills/writing-quarto/examples/07-layout-list.md +3 -3
- package/skills/writing-quarto/examples/08-appendix.md +1 -1
- package/skills/writing-quarto/quarto/authoring.md +7 -6
- package/skills/writing-quarto/quarto/basics.md +17 -16
- package/skills/writing-quarto/quarto/coords.md +2 -1
- package/skills/writing-quarto/quarto/figures.md +49 -0
- package/skills/writing-quarto/quarto/mermaid.md +41 -5
- package/skills/writing-quarto/quarto/prose.md +4 -4
- package/skills/writing-quarto/quarto/refactor.md +1 -1
- package/skills/writing-quarto/quarto/terminal.md +1 -1
- package/skills/writing-quarto/references/book-journey.md +38 -0
- package/skills/writing-quarto/references/token-budget.md +26 -0
- package/skills/writing-quarto/scripts/checking-callouts.py +2 -2
- package/skills/writing-quarto/scripts/checking-dom.py +12 -6
- package/skills/writing-quarto/scripts/checking-links.py +29 -13
- package/skills/writing-quarto/scripts/checking-names.py +3 -1
- package/skills/writing-quarto/scripts/checking-output.py +3 -3
- package/skills/writing-quarto/scripts/checking-prose.py +11 -7
- package/skills/writing-quarto/scripts/testing-content.py +264 -1
- package/skills/writing-quarto/scripts/verifying-content.py +442 -36
- package/skills/writing-quarto/steps/03-blocks.md +20 -8
- package/skills/writing-quarto/steps/04-paragraphs.md +19 -2
- package/skills/writing-quarto/steps/06-wording.md +53 -0
- package/skills/writing-quarto/steps/07-layout.md +1 -1
- package/skills/writing-quarto/steps/08-reread.md +1 -0
- package/skills/writing-quarto/steps/09-check.md +3 -3
- package/templates/quarto-book-project/SKILL.md +1 -1
- package/templates/quarto-book-project/checks.json +5 -0
package/README.en.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# dsh-quarto-book
|
|
2
2
|
|
|
3
3
|
[](https://github.com/chengzhao-dev/dsh-quarto-book/actions/workflows/publish.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@chengzhao-dev/dsh-quarto-book)
|
|
4
5
|
|
|
5
6
|
**English** | [简体中文](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/README.md)
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- Write `.qmd`: `writing-quarto` covers the chapter skeleton, structure and Chinese prose.
|
|
10
|
-
- Change the theme: `designing-theme` covers CSS, fonts and palettes.
|
|
11
|
-
- Check rendered HTML: `quarto_render_check` judges publishability once, and `quarto_pass_report` prints the closing receipt.
|
|
8
|
+
Take a Quarto Book from chapters to publishable output: `writing-quarto` owns the chapter skeleton, whole-book order and Chinese prose; `designing-theme` owns the theme; after a render, `quarto_render_check` decides whether the HTML is publishable and `quarto_pass_report` prints the closing receipt. Both skills appear only inside `type: book` repositories, and the workflow needs Quarto CLI and Python 3.
|
|
12
9
|
|
|
13
10
|
## What it provides
|
|
14
11
|
|
|
@@ -19,31 +16,51 @@ Polish Chinese technical chapters, check book structure and mixed Chinese-Englis
|
|
|
19
16
|
| `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
17
|
| `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
18
|
|
|
22
|
-
|
|
19
|
+
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/`, `references/`, `scripts/`; `designing-theme` uses `references/`, `knowledge/`, `assets/`, `scripts/`.
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
## Quick start
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
1. Install the plugin:
|
|
27
24
|
|
|
28
|
-
```bash
|
|
29
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@
|
|
30
|
-
```
|
|
25
|
+
```bash
|
|
26
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@latest
|
|
27
|
+
```
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
2. Open a session in the book repository. `_quarto.yml` needs a standalone `type: book` line, and the book needs at least one `.qmd` or `.md` chapter source; only then do both skill names appear.
|
|
30
|
+
3. Call a skill: `writing-quarto` for prose, `designing-theme` for the theme.
|
|
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 with `dsh plugin --profile desktop add <package>`, and use the app's Settings → Plugins → Add plugin only if the app refuses that write. To pin a version, write `npm:@chengzhao-dev/dsh-quarto-book@0.6.4`; to follow a GitHub tag, write `github:chengzhao-dev/dsh-quarto-book#v0.6.4`.
|
|
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
|
-
|
|
36
|
+
Installation is scoped to a profile, not to a repository: it only affects the profile that ran `add`. The workspace check 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.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
| Dependency | Purpose |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| DSH 0.2.x | Host; the plugin registers skills and both tools through `dsh.bundle.patch` |
|
|
43
|
+
| Node.js 22 or newer | Plugin runtime and check scripts |
|
|
44
|
+
| Python 3 | Content, link and naming checks, plus post-render HTML checks |
|
|
45
|
+
| Quarto CLI | Only when `quarto_render_check` actually renders |
|
|
46
|
+
|
|
47
|
+
The published package already contains compiled `lib/`, so installation needs no build step. See [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) for the split.
|
|
39
48
|
|
|
40
49
|
## Usage
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
### Writing chapters
|
|
52
|
+
|
|
53
|
+
Load `writing-quarto` in a `type: book` repository and follow the nine steps in `SKILL.md`. Each step opens one same-numbered file in `steps/`; read the same-numbered case in `examples/` only when a signal is unclear; open `quarto/` only for figures, render checks or repository-wide refactors.
|
|
54
|
+
|
|
55
|
+
The repository's root `README.md` is a description page, not a chapter: it takes only steps 5–8, skips the task skeleton and structure of steps 1–4, has no "common errors" or "self-test questions", and never calls `quarto_render_check`. `README.en.md` is treated the same way.
|
|
43
56
|
|
|
44
|
-
|
|
57
|
+
The plugin has no daemon and does not run skills when a session starts. A skill name in the catalog only means the check passed; only calling `skill`, `quarto_render_check` or `quarto_pass_report` counts as use. A second call in the same session re-reads the skill body, and failed tools are not retried automatically.
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
### Theming
|
|
60
|
+
|
|
61
|
+
Theme assets live in `skills/designing-theme/assets/theme/`. The package copy is authoritative; a book's `theme/` is the render copy. When a copy exists, `designing-theme` edits only that copy. It copies from the package only when the user explicitly asks to install the theme into the book and the target directory does not exist, and it also places `skills/writing-quarto/scripts/answering-disclosure.lua` at `filters/answering-disclosure.lua`. `_quarto.yml` always references the in-book copy, never an npm package or a checkout path.
|
|
62
|
+
|
|
63
|
+
### Checks and rendering
|
|
47
64
|
|
|
48
65
|
Run the content and link checks from the `writing-quarto` skill directory:
|
|
49
66
|
|
|
@@ -54,25 +71,31 @@ python scripts/checking-prose.py --root <book>
|
|
|
54
71
|
python scripts/checking-names.py --root <book>
|
|
55
72
|
```
|
|
56
73
|
|
|
57
|
-
`checking-prose.py` reports only
|
|
74
|
+
`checking-prose.py` reports only mixed Chinese-English problems that can be judged directly; `checking-names.py` is for repository-wide refactors. For everyday chapter work, the first three are enough.
|
|
75
|
+
|
|
76
|
+
Post-render callouts, HTML output and DOM contracts are checked by `quarto_render_check`, which calls the bundled scripts; run `checking-callouts.py`, `checking-output.py` or `checking-dom.py` by hand only when debugging a script. `designing-theme`'s `scripts/checking-layout.py` checks overflow, contrast and page structure on rendered HTML. Neither tool runs those scripts at load time, and neither runs `quarto render`.
|
|
77
|
+
|
|
78
|
+
Call `quarto_pass_report` once at the end of a run; the card is the receipt, and the reply should not repeat it.
|
|
58
79
|
|
|
59
|
-
|
|
80
|
+
### Project skill
|
|
60
81
|
|
|
61
|
-
|
|
82
|
+
A project keeps its own conventions in one skill: `<book>/.agents/skills/quarto-book-project/`, with a template in `templates/quarto-book-project/`. When it exists, both generic skills read its `SKILL.md` first, then the `writing/` files it points to; when it does not exist, they skip it and do not guess.
|
|
83
|
+
|
|
84
|
+
**Do not vendor a same-named skill into the book repository.** DSH picks one skill per name at its rank: `.agents/skills` (rank 200) wins over the plugin (rank 550), so a copy of `writing-quarto` or `designing-theme` inside the book silently replaces the plugin's rules.
|
|
62
85
|
|
|
63
86
|
## Documentation
|
|
64
87
|
|
|
65
88
|
| Document | Contents |
|
|
66
89
|
| --- | --- |
|
|
67
90
|
| [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 |
|
|
68
|
-
| [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) | The six workspace-
|
|
69
|
-
| [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) | Repository
|
|
91
|
+
| [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) | The six workspace-check cases and their order |
|
|
92
|
+
| [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) | Repository layout, package contents, and the Python/Quarto split |
|
|
70
93
|
| [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 |
|
|
71
94
|
| [docs/versioning.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/versioning.md) | Version numbers and release order |
|
|
72
95
|
|
|
73
|
-
##
|
|
96
|
+
## Development
|
|
74
97
|
|
|
75
|
-
The source is TypeScript and the compiled `lib/` is committed
|
|
98
|
+
The source is TypeScript and the compiled `lib/` is committed; the published package loads `lib/` directly and needs no build on install. Link this checkout into the profile you write books with instead of copying skills into the book:
|
|
76
99
|
|
|
77
100
|
```bash
|
|
78
101
|
npm ci
|
|
@@ -81,18 +104,24 @@ npm run check
|
|
|
81
104
|
npm run pack
|
|
82
105
|
```
|
|
83
106
|
|
|
84
|
-
`npm test`
|
|
107
|
+
`npm test` compiles and runs the tool tests; `npm run check` type-checks and validates skill paths; `npm run pack` writes the tarball into `temp/`, which is already in `.gitignore`.
|
|
108
|
+
|
|
109
|
+
Development install:
|
|
85
110
|
|
|
86
111
|
```bash
|
|
87
|
-
dsh plugin --profile web add D
|
|
112
|
+
dsh plugin --profile web add D:\\Github\\dsh-quarto-book
|
|
88
113
|
```
|
|
89
114
|
|
|
90
|
-
`SKILL.md`, `steps/`, `examples/` and `quarto/` are re-read on the next `skill`
|
|
115
|
+
`SKILL.md`, `steps/`, `examples/` and `quarto/` are re-read on the next `skill` call, so rule changes need no npm publish. Theme changes in the package do not reach a book's HTML by themselves: copy the changed files into the book's `theme/` and run `quarto render` in the book root. Do not point `_quarto.yml` at the checkout; that path does not exist on another machine.
|
|
116
|
+
|
|
117
|
+
The plugin ships as a bundle: `package.json` has `dsh.bundle.patch` pointing at `cordis.patch.yml`, and the patch inserts only the `id: quarto-book`, `name: '@chengzhao-dev/dsh-quarto-book'` line. The entry does not import host internals, so it also loads under pnpm's strict dependency layout.
|
|
118
|
+
|
|
119
|
+
The legacy `scripts/embedding-agents.ps1` copies both skill trees into a target repository's `.agents/`, which shadows the plugin at rank 200. Do not use it for new books; run it only to archive a standalone skill tree offline. The command is at the top of the script.
|
|
91
120
|
|
|
92
|
-
|
|
121
|
+
## Issues and contributing
|
|
93
122
|
|
|
94
|
-
|
|
123
|
+
Bugs and suggestions go to [GitHub Issues](https://github.com/chengzhao-dev/dsh-quarto-book/issues). Before sending a change, run `npm test` and `npm run check`; for skill prose, also run `node scripts/checking-skill-links.mjs`.
|
|
95
124
|
|
|
96
|
-
##
|
|
125
|
+
## License
|
|
97
126
|
|
|
98
|
-
MIT.
|
|
127
|
+
MIT. LXGW Bright and LXGW Bright Code in the theme are used under the SIL Open Font License 1.1; the upstream copyright notice and full license are in `skills/designing-theme/assets/theme/assets/fonts/OFL.txt`.
|
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# dsh-quarto-book
|
|
2
2
|
|
|
3
3
|
[](https://github.com/chengzhao-dev/dsh-quarto-book/actions/workflows/publish.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@chengzhao-dev/dsh-quarto-book)
|
|
4
5
|
|
|
5
6
|
[English](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/README.en.md) | **简体中文**
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- 写 `.qmd`:`writing-quarto` 管章节骨架、结构与中文行文。
|
|
10
|
-
- 改主题:`designing-theme` 管 CSS、字体与 palette。
|
|
11
|
-
- 渲染后检查 HTML:`quarto_render_check` 判一次可发布性,`quarto_pass_report` 出收尾回执。
|
|
8
|
+
把一份 Quarto Book 从章节写到可发布:`writing-quarto` 负责章节骨架、整书次序与中文行文,`designing-theme` 负责主题;渲染后由 `quarto_render_check` 判断 HTML 是否可发布,`quarto_pass_report` 给出收尾回执。两个技能只在 `type: book` 的仓库里出现,运行需要 Quarto CLI 与 Python 3。
|
|
12
9
|
|
|
13
10
|
## 提供什么
|
|
14
11
|
|
|
@@ -17,33 +14,53 @@
|
|
|
17
14
|
| `writing-quarto` | 新建或修改 `content/**/*.qmd`、`index.qmd`、`_quarto.yml`;章节骨架、中文行文、代码与图表、内容检查 | 不改 CSS、字体和颜色 |
|
|
18
15
|
| `designing-theme` | 调整主题的 CSS、SCSS、字体、palette、导航与 Callout 样式 | 不改章节正文 |
|
|
19
16
|
| `quarto_render_check` | 先判断 `output-dir` 是否安全,再按需渲染一次,检查 `_book/` 里的 HTML 是否可发布 | 不改 `.qmd`,不自动修配置,不重复渲染 |
|
|
20
|
-
| `quarto_pass_report` |
|
|
17
|
+
| `quarto_pass_report` | 收尾时把执行结论压成固定行数的回执卡片:技能、模式、改过的文件与步骤、未改项、检查结论 | 不保存到磁盘,不贴章节原文,不据回执改文章 |
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
两个技能从包内 `skills/` 目录发现,按需加载;技能里的相对路径以技能目录为基准:`writing-quarto` 用 `steps/`、`examples/`、`quarto/`、`references/`、`scripts/`,`designing-theme` 用 `references/`、`knowledge/`、`assets/`、`scripts/`。
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
## 快速开始
|
|
25
22
|
|
|
26
|
-
|
|
23
|
+
1. 安装插件:
|
|
27
24
|
|
|
28
|
-
```bash
|
|
29
|
-
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@
|
|
30
|
-
```
|
|
25
|
+
```bash
|
|
26
|
+
dsh plugin --profile web add npm:@chengzhao-dev/dsh-quarto-book@latest
|
|
27
|
+
```
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
2. 在书仓库里打开会话。`_quarto.yml` 要有独立一行的 `type: book`,书根至少有一个 `.qmd` 或 `.md` 章节源文件;两个技能名这时才会出现。
|
|
30
|
+
3. 调用技能:写正文用 `writing-quarto`,改主题用 `designing-theme`。
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
`--profile` 换成你实际运行的那个 profile。桌面应用固定跑 `desktop` profile,且该 profile 由应用独占管理,`dsh --profile desktop` 直接启动会被拒绝;安装或升级用 `dsh plugin --profile desktop add <包>`,应用拒绝写入时再走「设置 → 插件 → 添加插件」。需要固定版本时写 `npm:@chengzhao-dev/dsh-quarto-book@0.6.4`,跟 GitHub 标签时写 `github:chengzhao-dev/dsh-quarto-book#v0.6.4`。
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
从旧的无作用域包 `dsh-quarto-book` 升级(该包已随 0.3.4 从 npm 删除,只影响之前装过它的 profile):先在同一个 profile 里 `dsh plugin --profile <profile> remove dsh-quarto-book`,再装新包。两个包同时留着会以相同的 rank 550 各注册一遍 `writing-quarto` 和 `designing-theme`。
|
|
37
35
|
|
|
38
|
-
安装单位是 profile,不是仓库:只影响执行 `add` 的那个 profile
|
|
36
|
+
安装单位是 profile,不是仓库:只影响执行 `add` 的那个 profile。装好之后还要过工作区检查:按会话 `cwd` 判断当前目录是不是一本书,通过后技能名才会出现。检查的六类情形见 [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md)。
|
|
37
|
+
|
|
38
|
+
## 环境要求
|
|
39
|
+
|
|
40
|
+
| 依赖 | 用途 |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| DSH 0.2.x | 宿主;插件按 `dsh.bundle.patch` 注册技能与两个工具 |
|
|
43
|
+
| Node.js 22 及以上 | 运行插件与检查脚本 |
|
|
44
|
+
| Python 3 | 内容、链接、命名检查,以及渲染后的 HTML 检查 |
|
|
45
|
+
| Quarto CLI | 仅 `quarto_render_check` 真正渲染时需要 |
|
|
46
|
+
|
|
47
|
+
发布包已经带编译好的 `lib/`,安装后不需要构建。环境分工见 [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md)。
|
|
39
48
|
|
|
40
49
|
## 使用
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
### 写章节
|
|
52
|
+
|
|
53
|
+
在 `type: book` 的仓库里加载 `writing-quarto`,按 `SKILL.md` 的九步执行。每一步只打开同号的一个 `steps/` 文件;判不准才读 `examples/` 里同号的案例;要排图、查渲染或全仓重构才打开 `quarto/`。
|
|
54
|
+
|
|
55
|
+
书仓库根目录的 `README.md` 是说明页,不是章节:只走第 5–8 步,不套第 1–4 步的任务与结构骨架,不写「常见错误」「自测问题」,不调 `quarto_render_check`。`README.en.md` 同样处理。
|
|
43
56
|
|
|
44
|
-
|
|
57
|
+
插件没有常驻进程,也不会在会话开始时自动执行技能。技能目录里出现名字只说明当前会话过了检查;只有调用 `skill`、`quarto_render_check` 或 `quarto_pass_report` 才算一次使用。同一会话再次调用会重读技能正文,工具失败后不会自动重试。
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
### 改主题
|
|
60
|
+
|
|
61
|
+
主题资产在 `skills/designing-theme/assets/theme/`,包内是权威来源,书内 `theme/` 是渲染副本。已有副本时 `designing-theme` 只改副本;只有用户明确要求「把插件主题装进这本书」且目标目录不存在,才从包复制,并把 `skills/writing-quarto/scripts/answering-disclosure.lua` 放到书内 `filters/answering-disclosure.lua`。`_quarto.yml` 只引用书内副本,不指向 npm 包或插件检出目录。
|
|
62
|
+
|
|
63
|
+
### 检查与渲染
|
|
47
64
|
|
|
48
65
|
内容与链接检查在 `writing-quarto` 技能目录下运行:
|
|
49
66
|
|
|
@@ -56,21 +73,27 @@ python scripts/checking-names.py --root <book>
|
|
|
56
73
|
|
|
57
74
|
`checking-prose.py` 只报中文混排里能直接判断的问题;`checking-names.py` 只在全仓重构时加跑。日常章节检查跑前三条即可。
|
|
58
75
|
|
|
59
|
-
渲染后的 Callout、HTML 产物与 DOM
|
|
76
|
+
渲染后的 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`。
|
|
60
77
|
|
|
61
78
|
一轮结束时调用一次 `quarto_pass_report`,把这次执行的结论压成卡片;卡片就是回执,回复里不再复述。
|
|
62
79
|
|
|
80
|
+
### 项目技能
|
|
81
|
+
|
|
82
|
+
项目只放一份自己的约定技能:`<book>/.agents/skills/quarto-book-project/`,模板见 `templates/quarto-book-project/`。存在时两个通用技能先读它的 `SKILL.md`,再按它指向的 `writing/` 文件读章节表;不存在就跳过,不猜项目约定。
|
|
83
|
+
|
|
84
|
+
**不要把同名技能嵌进书仓库**:DSH 在同一技能层按 rank 取一个,`.agents/skills`(rank 200)优先于插件(rank 550),书里再放 `writing-quarto` 或 `designing-theme` 会让插件正文失效。
|
|
85
|
+
|
|
63
86
|
## 文档
|
|
64
87
|
|
|
65
88
|
| 文档 | 内容 |
|
|
66
89
|
| --- | --- |
|
|
67
90
|
| [docs/plugin-flow.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-flow.md) | 从安装到回执的五层结构、文件分流与渲染顺序 |
|
|
68
|
-
| [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) |
|
|
91
|
+
| [docs/plugin-boundaries.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-boundaries.md) | 工作区检查的六类情形与判定顺序 |
|
|
69
92
|
| [docs/plugin-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/plugin-layout.md) | 仓库目录、发布包内容与 Python/Quarto 分工 |
|
|
70
93
|
| [docs/writing-quarto-layout.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/writing-quarto-layout.md) | 写作技能的目录分层 |
|
|
71
94
|
| [docs/versioning.md](https://github.com/chengzhao-dev/dsh-quarto-book/blob/main/docs/versioning.md) | 版本号与发布顺序 |
|
|
72
95
|
|
|
73
|
-
##
|
|
96
|
+
## 开发
|
|
74
97
|
|
|
75
98
|
源码用 TypeScript,构建产物 `lib/` 提交进仓库;发布包直接加载 `lib/`,安装后不需要构建。开发时把本仓库链接进写书用的 profile,而不是把技能复制进书:
|
|
76
99
|
|
|
@@ -81,10 +104,12 @@ npm run check
|
|
|
81
104
|
npm run pack
|
|
82
105
|
```
|
|
83
106
|
|
|
84
|
-
`npm test` 会先编译再跑工具测试;`npm run check` 做类型检查与技能路径自检;`npm run pack` 把 tarball 只写进 `temp/`,那个目录已在 `.gitignore`
|
|
107
|
+
`npm test` 会先编译再跑工具测试;`npm run check` 做类型检查与技能路径自检;`npm run pack` 把 tarball 只写进 `temp/`,那个目录已在 `.gitignore` 里。
|
|
108
|
+
|
|
109
|
+
开发安装:
|
|
85
110
|
|
|
86
111
|
```bash
|
|
87
|
-
dsh plugin --profile web add D
|
|
112
|
+
dsh plugin --profile web add D:\\Github\\dsh-quarto-book
|
|
88
113
|
```
|
|
89
114
|
|
|
90
115
|
`SKILL.md`、`steps/`、`examples/`、`quarto/` 在下一次 `skill` 工具调用时重读,改完不用发 npm。包内主题改动不会自动进书的 HTML;要看页面效果,把改过的文件同步到书的 `theme/`,再在书根运行 `quarto render`。不要把 `_quarto.yml` 指到插件检出目录,那条路径换机器就不存在。
|
|
@@ -93,6 +118,10 @@ dsh plugin --profile web add D:\Github\dsh-quarto-book
|
|
|
93
118
|
|
|
94
119
|
旧装机脚本 `scripts/embedding-agents.ps1` 会把两棵技能原样复制进目标仓库的 `.agents/`,按 rank 200 盖住插件;新书不要用它,只有离线归档独立技能树时才执行,命令写在脚本头部。
|
|
95
120
|
|
|
121
|
+
## 问题与贡献
|
|
122
|
+
|
|
123
|
+
问题与建议走 [GitHub Issues](https://github.com/chengzhao-dev/dsh-quarto-book/issues)。提交改动前先运行 `npm test` 与 `npm run check`;涉及技能正文时,同时跑 `node scripts/checking-skill-links.mjs`。
|
|
124
|
+
|
|
96
125
|
## 许可
|
|
97
126
|
|
|
98
127
|
MIT。主题内的 LXGW Bright 与 LXGW Bright Code 按 SIL Open Font License 1.1 使用;上游版权声明与许可全文在 `skills/designing-theme/assets/theme/assets/fonts/OFL.txt`。
|
package/lib/index.js
CHANGED
|
@@ -24,15 +24,17 @@ const RANK = 550;
|
|
|
24
24
|
const SKILL_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
25
25
|
const PACKAGE_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
26
26
|
const SKILLS_ROOT = path.join(PACKAGE_ROOT, 'skills');
|
|
27
|
-
/** 读取 SKILL.md
|
|
27
|
+
/** 读取 SKILL.md 实际用到的几个键,只做最小的 YAML frontmatter 解析。 */
|
|
28
28
|
function parseFrontmatter(source) {
|
|
29
|
-
const
|
|
29
|
+
const normalized = source.replace(/^\uFEFF/, '');
|
|
30
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(normalized);
|
|
30
31
|
if (!match)
|
|
31
|
-
return { data: {}, body:
|
|
32
|
+
return { data: {}, body: normalized };
|
|
32
33
|
const data = {};
|
|
33
34
|
const stack = [{ indent: -1, target: data }];
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
const lines = match[1].split(/\r?\n/);
|
|
36
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
37
|
+
const line = lines[index].replace(/\t/g, ' ');
|
|
36
38
|
if (!line.trim() || line.trimStart().startsWith('#'))
|
|
37
39
|
continue;
|
|
38
40
|
const indent = line.length - line.trimStart().length;
|
|
@@ -43,16 +45,56 @@ function parseFrontmatter(source) {
|
|
|
43
45
|
stack.pop();
|
|
44
46
|
const parent = stack[stack.length - 1].target;
|
|
45
47
|
const key = pair[1];
|
|
46
|
-
const value = (pair[2] ?? '').trim();
|
|
48
|
+
const value = stripInlineComment(pair[2] ?? '').trim();
|
|
47
49
|
if (value === '') {
|
|
48
50
|
const child = {};
|
|
49
51
|
parent[key] = child;
|
|
50
52
|
stack.push({ indent, target: child });
|
|
51
53
|
continue;
|
|
52
54
|
}
|
|
55
|
+
if (/^[>|][+-]?\d?$/.test(value)) {
|
|
56
|
+
const block = [];
|
|
57
|
+
let cursor = index + 1;
|
|
58
|
+
for (; cursor < lines.length; cursor += 1) {
|
|
59
|
+
const next = lines[cursor].replace(/\t/g, ' ');
|
|
60
|
+
if (!next.trim()) {
|
|
61
|
+
block.push('');
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const nextIndent = next.length - next.trimStart().length;
|
|
65
|
+
if (nextIndent <= indent)
|
|
66
|
+
break;
|
|
67
|
+
block.push(next);
|
|
68
|
+
}
|
|
69
|
+
const nonBlank = block.filter((item) => item.trim());
|
|
70
|
+
const cut = nonBlank.length > 0 ? Math.min(...nonBlank.map((item) => item.length - item.trimStart().length)) : 0;
|
|
71
|
+
const text = block.map((item) => item.slice(cut)).join('\n');
|
|
72
|
+
parent[key] = value[0] === '|' ? text : text.replace(/([^\n])\n(?=[^\n])/g, '$1 ').replace(/\n{2,}/g, '\n');
|
|
73
|
+
index = cursor - 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
53
76
|
parent[key] = parseScalar(value);
|
|
54
77
|
}
|
|
55
|
-
return { data, body:
|
|
78
|
+
return { data, body: normalized.slice(match[0].length) };
|
|
79
|
+
}
|
|
80
|
+
/** 去掉 plain scalar 末尾的 ` # 注释`;引号内的 # 保留。 */
|
|
81
|
+
function stripInlineComment(value) {
|
|
82
|
+
let quote;
|
|
83
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
84
|
+
const char = value[index];
|
|
85
|
+
if (quote) {
|
|
86
|
+
if (char === quote && value[index - 1] !== '\\')
|
|
87
|
+
quote = undefined;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (char === '"' || char === "'") {
|
|
91
|
+
quote = char;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (char === '#' && (index === 0 || /\s/.test(value[index - 1])))
|
|
95
|
+
return value.slice(0, index);
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
56
98
|
}
|
|
57
99
|
function parseScalar(value) {
|
|
58
100
|
if ((value.startsWith('"') && value.endsWith('"')) ||
|
package/lib/tools/pass-report.js
CHANGED
|
@@ -74,6 +74,7 @@ function normalizeFiles(value) {
|
|
|
74
74
|
fail('files 必须是数组');
|
|
75
75
|
if (value.length > MAX_FILES)
|
|
76
76
|
fail(`files 最多 ${MAX_FILES} 个路径`);
|
|
77
|
+
const seen = new Set();
|
|
77
78
|
return value.map((item) => {
|
|
78
79
|
if (!isRecord(item))
|
|
79
80
|
fail('files 的每一项必须是 { path, edits }');
|
|
@@ -85,7 +86,11 @@ function normalizeFiles(value) {
|
|
|
85
86
|
fail('files 的 path 必须是非空字符串');
|
|
86
87
|
if (!isPlainCount(item.edits))
|
|
87
88
|
fail('files 的 edits 必须是非负整数');
|
|
88
|
-
|
|
89
|
+
const normalizedPath = filePath.trim();
|
|
90
|
+
if (seen.has(normalizedPath))
|
|
91
|
+
fail(`files 的 path 重复:${normalizedPath}`);
|
|
92
|
+
seen.add(normalizedPath);
|
|
93
|
+
return { path: normalizedPath, edits: item.edits };
|
|
89
94
|
});
|
|
90
95
|
}
|
|
91
96
|
function normalizeSteps(value, skill) {
|
|
@@ -194,17 +199,12 @@ export function buildPassReport(input) {
|
|
|
194
199
|
fail('未执行时必须给出 skip_reason');
|
|
195
200
|
if ([...reason.trim()].length > MAX_SKIP_REASON)
|
|
196
201
|
fail(`skip_reason 最多 ${MAX_SKIP_REASON} 个字`);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
if (input.held !== undefined && Array.isArray(input.held) && input.held.length > 0) {
|
|
204
|
-
fail('未执行时 held 必须为空');
|
|
205
|
-
}
|
|
206
|
-
if (input.checks !== undefined && Array.isArray(input.checks) && input.checks.length > 0) {
|
|
207
|
-
fail('未执行时 checks 必须为空');
|
|
202
|
+
for (const key of ['files', 'steps', 'held', 'checks']) {
|
|
203
|
+
const value = input[key];
|
|
204
|
+
if (value === undefined)
|
|
205
|
+
continue;
|
|
206
|
+
if (!Array.isArray(value) || value.length > 0)
|
|
207
|
+
fail(`未执行时 ${key} 必须为空数组`);
|
|
208
208
|
}
|
|
209
209
|
return { receipt: `未执行 ${typedSkill}:${reason.trim()}` };
|
|
210
210
|
}
|