@chengzhao-dev/dsh-quarto-book 0.6.8 → 0.6.9
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 +1 -1
- package/README.md +1 -1
- package/package.json +1 -1
- package/skills/writing-quarto/examples/04-paragraph-cases.md +18 -0
- package/skills/writing-quarto/examples/05-sentence-frame.md +12 -0
- package/skills/writing-quarto/scripts/verifying-content.py +29 -2
- package/skills/writing-quarto/steps/04-paragraphs.md +1 -0
- package/skills/writing-quarto/steps/05-sentences.md +2 -1
- package/skills/writing-quarto/steps/08-reread.md +1 -1
package/README.en.md
CHANGED
|
@@ -29,7 +29,7 @@ Both skills are discovered from the package's `skills/` directory and loaded on
|
|
|
29
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
30
|
3. Call a skill: `writing-quarto` for prose, `designing-theme` for the theme.
|
|
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 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.
|
|
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.9`; to follow a GitHub tag, write `github:chengzhao-dev/dsh-quarto-book#v0.6.9`.
|
|
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
|
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
2. 在书仓库里打开会话。`_quarto.yml` 要有独立一行的 `type: book`,书根至少有一个 `.qmd` 或 `.md` 章节源文件;两个技能名这时才会出现。
|
|
30
30
|
3. 调用技能:写正文用 `writing-quarto`,改主题用 `designing-theme`。
|
|
31
31
|
|
|
32
|
-
`--profile` 换成你实际运行的那个 profile。桌面应用固定跑 `desktop` profile,且该 profile 由应用独占管理,`dsh --profile desktop` 直接启动会被拒绝;安装或升级用 `dsh plugin --profile desktop add <包>`,应用拒绝写入时再走「设置 → 插件 → 添加插件」。需要固定版本时写 `npm:@chengzhao-dev/dsh-quarto-book@0.6.
|
|
32
|
+
`--profile` 换成你实际运行的那个 profile。桌面应用固定跑 `desktop` profile,且该 profile 由应用独占管理,`dsh --profile desktop` 直接启动会被拒绝;安装或升级用 `dsh plugin --profile desktop add <包>`,应用拒绝写入时再走「设置 → 插件 → 添加插件」。需要固定版本时写 `npm:@chengzhao-dev/dsh-quarto-book@0.6.9`,跟 GitHub 标签时写 `github:chengzhao-dev/dsh-quarto-book#v0.6.9`。
|
|
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
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chengzhao-dev/dsh-quarto-book",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.9",
|
|
4
4
|
"description": "把一份 Quarto Book 写成可读、可查、可发布的中文技术书:九步章节写作、结构/链接/图表检查、统一主题与渲染验收。 · Write, check, and publish a Chinese Quarto Book: nine-step chapter writing, structure/link/figure checks, a consistent theme, and render acceptance.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -112,3 +112,21 @@
|
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
自查:删掉重复句后,读者还缺哪一条事实?只补缺的那条;「数量不变」「出界不写入」若已在类注释或 `IsInRange` 的契约里写过,就不再复述。
|
|
115
|
+
|
|
116
|
+
## 混段:函数、终端与测试
|
|
117
|
+
|
|
118
|
+
不佳(一段里同时讲读接口、终端和测试):
|
|
119
|
+
|
|
120
|
+
`GetCellType`、`IsCellEmpty`、`IsInRange` 负责读格子;终端用 `ReadHumanMove` 和 `PrintGame` 显示棋盘;测试用 `gtest_discover_tests` 注册六组用例。
|
|
121
|
+
|
|
122
|
+
改写(先一句导语,再一项一件):
|
|
123
|
+
|
|
124
|
+
`PlaceCell` 尝试把棋子写进棋盘,写入前先检查坐标与目标格。
|
|
125
|
+
|
|
126
|
+
- `IsInRange`:检查坐标是否落在棋盘内。
|
|
127
|
+
- `IsCellEmpty`:检查目标格是否为空。
|
|
128
|
+
- `PlaceCell`:通过两项检查后写入棋子,失败时棋盘不变。
|
|
129
|
+
|
|
130
|
+
终端读入、终端打印和测试注册各自另起一段或另起列表。
|
|
131
|
+
|
|
132
|
+
判定信号:一段里出现三个以上函数名,且分句之间没有「因此」这类递进关系;读者无法从中挑出下一步要改的那一处。
|
|
@@ -27,6 +27,18 @@
|
|
|
27
27
|
|
|
28
28
|
判定信号:设计说明句里选择先于依据,读者要把因果倒过来才能读;「是规则的一部分」这类只贴类别、不改变下一步的分句;删掉关系词后读者仍要自己补因果;「正好装下」在同段复述同一核;后续小节专讲的对象在本段末尾写成「由 X 完成」。
|
|
29
29
|
|
|
30
|
+
## 技能腔:解释作者的选择
|
|
31
|
+
|
|
32
|
+
不佳(把「这里不定义自己的异常类型」写进正文,读者没有下一步动作):
|
|
33
|
+
|
|
34
|
+
`GetCellType` 越界时抛出 `std::out_of_range`,消息里带行号和列号。这里不定义自己的异常类型:调用方用 `std::out_of_range` 就能接住,测试也不必为项目再记一个类型名。
|
|
35
|
+
|
|
36
|
+
改写(只留读者看得到的结果):
|
|
37
|
+
|
|
38
|
+
`GetCellType` 越界时抛出 `std::out_of_range`,消息里带行号和列号。
|
|
39
|
+
|
|
40
|
+
判定信号:句子在解释作者为什么不另造类型、测试为什么少记一个名字、注释该怎么组织;删掉后读者执行的动作不变。
|
|
41
|
+
|
|
30
42
|
## 拉链句
|
|
31
43
|
|
|
32
44
|
一句话把两组对象两两配对,删掉任一配对、剩下的仍能单独成立,原句就是拉链句:读者要按顺序把左组一一配到右组,且两个配对共用同一个判据(判定与三条改写见 `steps/05-sentences.md`「拉链句」;步骤链、同物两处与回顾不按拉链判)。以 示例章章首改稿前与 示例章改稿前为对照:
|
|
@@ -54,6 +54,16 @@ COINED_RELATION_RE = re.compile(r"(?:组成|依赖|继承|调用)边")
|
|
|
54
54
|
CARRIER_RE = re.compile("承载")
|
|
55
55
|
NEXT_SECTION_RE = re.compile(r"(?:下一节|上一节)")
|
|
56
56
|
LADDER_RE = re.compile(r"(?:分别交给|各说一层)")
|
|
57
|
+
SKILL_TONE_RE = re.compile(r"这里不定义|测试也不必|不必为[^。;\n]{0,20}再记|不写双层循环")
|
|
58
|
+
INLINE_NAME_RE = re.compile(r"`([A-Za-z_][A-Za-z0-9_]*)`")
|
|
59
|
+
FUNCTION_NAME_RE = re.compile(
|
|
60
|
+
r"^(?:Is|Get|Set|Has|Can|Completes|Place|Read|Write|Print|Run|Make|Collect|Judge|"
|
|
61
|
+
r"Score|Search|Choose|Add|Clear|Reset|Find|Build|Parse|Format|Render|Report|Show|"
|
|
62
|
+
r"Start|Stop|Create|Destroy|Update|Check|Validate|To)[A-Z0-9_]"
|
|
63
|
+
r"|^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$"
|
|
64
|
+
)
|
|
65
|
+
TERMINAL_RE = re.compile(r"终端|打印|显示|输入|读入|命令行")
|
|
66
|
+
TEST_RE = re.compile(r"测试|断言|用例|gtest|CTest")
|
|
57
67
|
|
|
58
68
|
TREE_CATEGORY_COMMENTS = frozenset({
|
|
59
69
|
"头文件", "源文件", "源码", "实现", "测试", "配置文件",
|
|
@@ -196,12 +206,27 @@ def check_heading(path: Path, line: int, level: int, text: str, issues: list[Iss
|
|
|
196
206
|
issues.append(Issue(path, line, "标题带行内代码;文件名留给正文,标题写对象"))
|
|
197
207
|
|
|
198
208
|
|
|
199
|
-
def check_prose_block(
|
|
209
|
+
def check_prose_block(
|
|
210
|
+
path: Path, start_line: int, text: str, issues: list[Issue], is_list: bool = False
|
|
211
|
+
) -> None:
|
|
200
212
|
compact = text.strip()
|
|
201
213
|
if not compact:
|
|
202
214
|
return
|
|
203
215
|
if CHAPTER_NUMBER_RE.search(compact):
|
|
204
216
|
issues.append(Issue(path, start_line, "第 N 章式编号指称(用章节标题或链接)"))
|
|
217
|
+
if SKILL_TONE_RE.search(compact):
|
|
218
|
+
issues.append(Issue(path, start_line, "技能腔:只约束作者的选择,整句删掉"))
|
|
219
|
+
if not is_list and CJK_RE.search(compact):
|
|
220
|
+
names = {
|
|
221
|
+
match.group(1)
|
|
222
|
+
for match in INLINE_NAME_RE.finditer(compact)
|
|
223
|
+
if FUNCTION_NAME_RE.search(match.group(1))
|
|
224
|
+
}
|
|
225
|
+
if len(names) >= 3 and TERMINAL_RE.search(compact) and TEST_RE.search(compact):
|
|
226
|
+
issues.append(Issue(
|
|
227
|
+
path, start_line,
|
|
228
|
+
f"一段里出现 {len(names)} 个函数名,还同时讲终端与测试;拆成列表,一项一件",
|
|
229
|
+
))
|
|
205
230
|
if CJK_RE.search(compact):
|
|
206
231
|
length = len(re.sub(r"\s+", "", compact))
|
|
207
232
|
if length > MAX_PARAGRAPH_CHARS:
|
|
@@ -569,7 +594,9 @@ def check_file(path: Path, root: Path, project: dict, chapters: list[str]) -> li
|
|
|
569
594
|
|
|
570
595
|
if raw.lstrip().startswith((">", "-", "*", "+", "1.")):
|
|
571
596
|
flush_paragraph()
|
|
572
|
-
check_prose_block(
|
|
597
|
+
check_prose_block(
|
|
598
|
+
path, number, re.sub(r"^\s*(?:[>|]|[-*+]|\d+\.)\s*", "", raw), issues, is_list=True
|
|
599
|
+
)
|
|
573
600
|
continue
|
|
574
601
|
|
|
575
602
|
if not paragraph:
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
| --- | --- |
|
|
35
35
|
| 段内出现第二个互不依赖的对象或主题 | 新起段;新段首句接住上段对象(衔接见本文件「段间:过渡句只在话题切换处」) |
|
|
36
36
|
| 段内出现「话题标签:解释」词条式冒号,或两个以上互不依赖的中心并列推进 | 按「拆核四步(多核混段的拆段方法)」判关系:递进同段衔接推进,并列拆段;词条式冒号改完整主谓句(见「散文伪标签冒号禁令」) |
|
|
37
|
+
| 一段同时讲多个函数、终端打印和测试核对,句子互不从属 | 改成列表,一项一个函数或一种调用场合;正反例见 `examples/04-paragraph-cases.md`「混段:函数、终端与测试」 |
|
|
37
38
|
| 一段同时承担「承接上一章」+「本章交付物清单」+「源文件位置」+「摘录声明」 | 章引言不拆段,形态硬限与摘录声明删除见 `steps/02-structure.md`;其他段落按职责拆段,交付物多项且需扫读 → 列表(目录式堆砌对照见 `examples/02-structure-shape.md`) |
|
|
38
39
|
| 超过约 4 行舒适带、或逼近 7 行 / 篇幅 token 上限 | 先找逻辑边界拆,禁止为凑行数在句中腰斩 |
|
|
39
40
|
| 连续 ≥3 段纯散文且无列表/代码/图 | 改写见本文件「句序与段落形态」 |
|
|
@@ -35,12 +35,13 @@
|
|
|
35
35
|
- 每个分句只担任一种角色:可观察事实、不变约束、因此作出的选择。次序固定为**事实 → 约束 → 因此选择**;选择句不得出现在依据之前——先写「长度取常量」再补「九个格子」,读者要把因果倒过来读。
|
|
36
36
|
- 光有回指不够。后一分句若不是同一谓语的延续,必须写出「因此 / 所以」;删掉关系词后读者仍要自己补因果,判为不合格。可观察事实(「状态面板一共九个格子」)可以在前,读者据此才知道选择成立。
|
|
37
37
|
- 「是示例项目规则的一部分」这类只贴类别、不改变下一步的分句删除:它不回答「所以代码怎么办」。
|
|
38
|
+
- 「因此选择」必须是读者下一步要执行的动作。只约束作者的选择(不另造类型、测试少记一个名字、注释该怎么组织)整句删掉,不改写成另一句解释;正文不展开与本章对象无关的标准库机制,也不写「不写甲,而是乙」。
|
|
38
39
|
- 一句里用逗号甩出的独立判断,最多留「事实,约束」或「约束,所以选择」一对;第三个判断另起句或删除。单句仍守本文件「句子规则」的 40 字与一动作上限。
|
|
39
40
|
- 递进同一核不拆段复述:长度和装下它的数组是同一个核,同段用「因此」接住,不另起一段再说一遍「正好装下」。
|
|
40
41
|
- 后续小节专讲的对象,本段不写「由 X 完成」:换算如果由下一小节专讲,本段不提前交接(「换算由下一小节完成」);块内注释或下节首句已经说出的职责,整句删除。
|
|
41
42
|
- 与「对象先行」的对齐方式见 `steps/04-paragraphs.md`「句序与段落形态」。
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
判定信号:设计说明句里选择先于依据;逗号甩出第三个独立判断;分句只贴类别不改变下一步;删掉关系词后因果要读者自补;段末提前交接后续小节的对象;「因此选择」只约束作者、读者没有下一步动作。改写对照见 `examples/05-sentence-frame.md`「句内骨架:先依据后选择」与「技能腔:解释作者的选择」。类型接线句与被下一句否定的全称按本文件「一次对话」判删留,不按本节改写句内次序。
|
|
44
45
|
|
|
45
46
|
### 拉链句
|
|
46
47
|
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
| 定句子 | 节首句以「这些/这个/这一步」作话题且同句列不出对象名(所指要等后面的 `###` 或代码围栏) | 节首同句列出对象名;已逐个写出名字后的回指保留 | `steps/05-sentences.md`「代词不能代替未写出的对象」、`examples/05-sentence-reference.md`「定结构与定句子:代词不能代替未写出的对象」 |
|
|
32
32
|
| 定句子 | 类型接线句(两个逗号分句各自是「成员叫法 + 使用 + 某一章的类型」,且两项都已在紧邻导语与围栏出现) | 整段删除,不走拉链的推迟、拆句、一项一行 | `steps/05-sentences.md`「一次对话」、`examples/04-paragraph-cases.md`「定段落:类型接线段整段删除」 |
|
|
33
33
|
| 定句子 | 一句里的全称(全部、所有、唯一)被下一句否定 | 改范围或删除;全称读完下一句仍成立才保留 | `steps/05-sentences.md`「一次对话」 |
|
|
34
|
-
| 定句子 | 先决定后补依据 / 逗号甩判断(一句用逗号并列三个以上独立判断)/ 「由 X 完成」式被动交接 | 按「句内骨架」改为事实 → 约束 →
|
|
34
|
+
| 定句子 | 先决定后补依据 / 逗号甩判断(一句用逗号并列三个以上独立判断)/ 「由 X 完成」式被动交接 | 按「句内骨架」改为事实 → 约束 → 读者下一步要执行的动作;只约束作者的选择整句删除;「由 X 完成」在定用词改主动句 | `steps/05-sentences.md`「句内骨架」 |
|
|
35
35
|
| 定句子 | 元叙述开场(「是规则,代码把它写成」「因此记作/写成 `class`」) | 对象先行:改为「对象 → 名称 → 约束 → 出处」 | `steps/06-wording.md`、`examples/01-language-scope.md` |
|
|
36
36
|
| 定句子 | 拉链句(两组对象配对,删任一仍成立;「分别交给/各说一层/分别是」) | 按推迟 → 拆句 → 一项一行改写 | `steps/05-sentences.md`「拉链句」、`examples/05-sentence-frame.md`「拉链句」 |
|
|
37
37
|
| 定句子 | 假对比「这不仅是 X,更是 Y」两半并不都提供信息;起跑式铺垫(「在开始之前」「首先我们来看」);句尾把一次操作拔高成意义 | 删宣布与拔高;两半都有信息时保留对比 | `steps/05-sentences.md`「情况:假对比、起跑式铺垫与意义拔高」 |
|