@seanyao/roll 2026.521.2 → 2026.522.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/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/bin/roll +913 -48
- package/lib/__pycache__/roll-loop-status.cpython-314.pyc +0 -0
- package/lib/__pycache__/roll_render.cpython-314.pyc +0 -0
- package/lib/__pycache__/slides-render.cpython-314.pyc +0 -0
- package/lib/roll-help.py +1 -0
- package/lib/roll-loop-status.py +37 -3
- package/lib/roll_render.py +20 -1
- package/lib/slides-render.py +488 -0
- package/lib/slides-validate.py +169 -0
- package/package.json +1 -1
- package/skills/roll-.changelog/SKILL.md +19 -17
- package/skills/roll-.dream/SKILL.md +1 -1
- package/skills/roll-deck/SKILL.md +136 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
US-DECK-002: deck.md schema + grounding validator.
|
|
4
|
+
|
|
5
|
+
Reads a `deck.md` file, parses it with the same parser used by the renderer
|
|
6
|
+
(`lib/slides-render.py`), and verifies:
|
|
7
|
+
|
|
8
|
+
1. Required frontmatter fields are present:
|
|
9
|
+
template, slug, title_en, title_zh, total_slides, created
|
|
10
|
+
2. frontmatter.total_slides matches the actual `## Slide N` section count.
|
|
11
|
+
3. Each slide has non-empty title_en / title_zh / body_en / body_zh.
|
|
12
|
+
4. Grounding threshold: at least ceil(N/3) evidence citations across all
|
|
13
|
+
slides (i.e. >= 1 per 3 slides). If the deck has fewer, the validator
|
|
14
|
+
exits non-zero with a ⚠️ grounding warning so callers (e.g.
|
|
15
|
+
`roll slides build`) can flag it.
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
python3 slides-validate.py <deck.md>
|
|
19
|
+
|
|
20
|
+
Exit codes:
|
|
21
|
+
0 valid (schema OK + grounding threshold met)
|
|
22
|
+
1 schema error (missing field, mismatch, missing slide body, etc.)
|
|
23
|
+
2 grounding warning (schema OK but evidence below threshold)
|
|
24
|
+
3 file not found / unreadable / parse error
|
|
25
|
+
|
|
26
|
+
Error messages are written to stderr in English + Chinese (Roll bilingual
|
|
27
|
+
convention).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import importlib.util
|
|
33
|
+
import math
|
|
34
|
+
import sys
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
REQUIRED_FRONTMATTER = (
|
|
39
|
+
"template",
|
|
40
|
+
"slug",
|
|
41
|
+
"title_en",
|
|
42
|
+
"title_zh",
|
|
43
|
+
"total_slides",
|
|
44
|
+
"created",
|
|
45
|
+
)
|
|
46
|
+
REQUIRED_SLIDE_KEYS = ("title_en", "title_zh", "body_en", "body_zh")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _load_renderer():
|
|
50
|
+
"""Import lib/slides-render.py as a module (hyphenated filename, so we
|
|
51
|
+
can't `import slides_render` directly)."""
|
|
52
|
+
here = Path(__file__).resolve().parent
|
|
53
|
+
spec = importlib.util.spec_from_file_location(
|
|
54
|
+
"slides_render", str(here / "slides-render.py")
|
|
55
|
+
)
|
|
56
|
+
if spec is None or spec.loader is None:
|
|
57
|
+
raise ImportError("could not load slides-render.py")
|
|
58
|
+
mod = importlib.util.module_from_spec(spec)
|
|
59
|
+
spec.loader.exec_module(mod)
|
|
60
|
+
return mod
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def err(msg_en: str, msg_zh: str = "") -> None:
|
|
64
|
+
print(f"[slides-validate] {msg_en}", file=sys.stderr)
|
|
65
|
+
if msg_zh:
|
|
66
|
+
print(f"[slides-validate] {msg_zh}", file=sys.stderr)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def validate_frontmatter(fm: dict) -> list[str]:
|
|
70
|
+
errors: list[str] = []
|
|
71
|
+
for key in REQUIRED_FRONTMATTER:
|
|
72
|
+
if key not in fm or fm[key] == "" or fm[key] is None:
|
|
73
|
+
errors.append(f"missing required frontmatter field: {key}")
|
|
74
|
+
if "total_slides" in fm and not isinstance(fm["total_slides"], int):
|
|
75
|
+
errors.append(
|
|
76
|
+
f"total_slides must be an integer, got "
|
|
77
|
+
f"{type(fm['total_slides']).__name__}: {fm['total_slides']!r}"
|
|
78
|
+
)
|
|
79
|
+
return errors
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def validate_slides(fm: dict, slides: list[dict]) -> list[str]:
|
|
83
|
+
errors: list[str] = []
|
|
84
|
+
actual = len(slides)
|
|
85
|
+
declared = fm.get("total_slides")
|
|
86
|
+
if isinstance(declared, int) and declared != actual:
|
|
87
|
+
errors.append(
|
|
88
|
+
f"total_slides mismatch: frontmatter declares {declared} but "
|
|
89
|
+
f"found {actual} `## Slide N` sections"
|
|
90
|
+
)
|
|
91
|
+
for slide in slides:
|
|
92
|
+
n = slide.get("number", "?")
|
|
93
|
+
for key in REQUIRED_SLIDE_KEYS:
|
|
94
|
+
v = slide.get(key)
|
|
95
|
+
if v is None or (isinstance(v, str) and v.strip() == ""):
|
|
96
|
+
errors.append(f"slide {n}: missing or empty {key}")
|
|
97
|
+
return errors
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def evaluate_grounding(slides: list[dict]) -> tuple[int, int, bool]:
|
|
101
|
+
"""
|
|
102
|
+
Return (citations, threshold, meets_threshold).
|
|
103
|
+
|
|
104
|
+
The threshold is `ceil(len(slides) / 3)` — i.e. at least one evidence
|
|
105
|
+
citation per three slides. An empty deck trivially meets the threshold
|
|
106
|
+
(threshold = 0).
|
|
107
|
+
"""
|
|
108
|
+
citations = 0
|
|
109
|
+
for slide in slides:
|
|
110
|
+
ev = slide.get("evidence")
|
|
111
|
+
if isinstance(ev, list):
|
|
112
|
+
citations += len(ev)
|
|
113
|
+
threshold = math.ceil(len(slides) / 3) if slides else 0
|
|
114
|
+
return citations, threshold, citations >= threshold
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def main(argv: list[str]) -> int:
|
|
118
|
+
if len(argv) < 2:
|
|
119
|
+
err(
|
|
120
|
+
"usage: slides-validate.py <deck.md>",
|
|
121
|
+
"用法: slides-validate.py <deck.md>",
|
|
122
|
+
)
|
|
123
|
+
return 3
|
|
124
|
+
|
|
125
|
+
path = Path(argv[1])
|
|
126
|
+
if not path.is_file():
|
|
127
|
+
err(f"deck file not found: {path}", f"未找到 deck 文件:{path}")
|
|
128
|
+
return 3
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
renderer = _load_renderer()
|
|
132
|
+
except Exception as e:
|
|
133
|
+
err(f"could not load renderer module: {e}")
|
|
134
|
+
return 3
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
src = path.read_text(encoding="utf-8")
|
|
138
|
+
fm, body = renderer.parse_frontmatter(src)
|
|
139
|
+
slides = renderer.parse_slides(body)
|
|
140
|
+
except (ValueError, OSError) as e:
|
|
141
|
+
err(f"failed to parse deck.md: {e}", "解析 deck.md 失败")
|
|
142
|
+
return 3
|
|
143
|
+
|
|
144
|
+
schema_errors: list[str] = []
|
|
145
|
+
schema_errors += validate_frontmatter(fm)
|
|
146
|
+
schema_errors += validate_slides(fm, slides)
|
|
147
|
+
|
|
148
|
+
if schema_errors:
|
|
149
|
+
for e in schema_errors:
|
|
150
|
+
err(e)
|
|
151
|
+
return 1
|
|
152
|
+
|
|
153
|
+
citations, threshold, ok = evaluate_grounding(slides)
|
|
154
|
+
if not ok:
|
|
155
|
+
err(
|
|
156
|
+
f"⚠️ grounding below threshold: {citations} evidence citation(s) "
|
|
157
|
+
f"for {len(slides)} slides (need >= {threshold}). "
|
|
158
|
+
f"Each slide group of 3 must include at least one evidence entry.",
|
|
159
|
+
f"⚠️ 证据引用不足:{len(slides)} 张幻灯片仅有 {citations} 条 evidence,"
|
|
160
|
+
f"至少需要 {threshold} 条(每 3 张 ≥ 1 条)。",
|
|
161
|
+
)
|
|
162
|
+
return 2
|
|
163
|
+
|
|
164
|
+
# Valid — silent success.
|
|
165
|
+
return 0
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
sys.exit(main(sys.argv))
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@ After successful Build & Deploy, extracts completed Stories from .roll/backlog.m
|
|
|
19
19
|
|
|
20
20
|
- Generating commit messages or PR descriptions — this skill only runs post-deploy
|
|
21
21
|
- Recording dev diary / moments (use `$roll-notes`)
|
|
22
|
-
- Bumping package version (use the project's release script
|
|
22
|
+
- Bumping package version (use the project's release script — Roll's lives in `roll-meta/ops/release.sh`)
|
|
23
23
|
|
|
24
24
|
## Workflow
|
|
25
25
|
|
|
@@ -59,7 +59,7 @@ CHANGELOG 只取破折号前的用户症状,丢弃破折号后的修复手段
|
|
|
59
59
|
- 测试基建(teardown 清理、test isolation、bats helper、CI 时序)
|
|
60
60
|
- prompt / SKILL.md 内部契约(schema 锁定、enum 强制、contract test)
|
|
61
61
|
- 内部重构(提取函数、变量改名、目录调整)
|
|
62
|
-
- 只有开发者会遇到的 bug
|
|
62
|
+
- 只有开发者会遇到的 bug(发版脚本自身逻辑、TCR 节奏调整)
|
|
63
63
|
- 任何"用户体验不变"的改动
|
|
64
64
|
|
|
65
65
|
判断准则:**如果用户读了这条记录,他不会改变使用方式,就别写。**
|
|
@@ -124,9 +124,10 @@ Fix 类句式参考:`<命令/功能> 不再 <之前的坏现象>`,或 `<命
|
|
|
124
124
|
|
|
125
125
|
### 4. Section Header — Always `## Unreleased`
|
|
126
126
|
|
|
127
|
-
**⚠️ do NOT guess version numbers.** Only
|
|
128
|
-
versions, and it only does so at
|
|
129
|
-
|
|
127
|
+
**⚠️ do NOT guess version numbers.** Only the release script (maintainer-private
|
|
128
|
+
in `roll-meta/ops/release.sh`) assigns concrete versions, and it only does so at
|
|
129
|
+
the moment of a real release. Until then, every new bullet goes under
|
|
130
|
+
`## Unreleased` at the top of CHANGELOG.md.
|
|
130
131
|
|
|
131
132
|
```
|
|
132
133
|
## Unreleased
|
|
@@ -134,8 +135,8 @@ every new bullet goes under `## Unreleased` at the top of CHANGELOG.md.
|
|
|
134
135
|
- **Fixed**: ...
|
|
135
136
|
```
|
|
136
137
|
|
|
137
|
-
When
|
|
138
|
-
computed from git tags) — that's the single moment a version label gets
|
|
138
|
+
When the release script runs, it renames `## Unreleased` to `## v{N}` (where N
|
|
139
|
+
is computed from git tags) — that's the single moment a version label gets
|
|
139
140
|
assigned.
|
|
140
141
|
|
|
141
142
|
Do NOT read `package.json` version, do NOT call `git describe`, do NOT invent
|
|
@@ -291,8 +292,8 @@ After successful deploy in `$roll-build` / `$roll-fix`:
|
|
|
291
292
|
|
|
292
293
|
## 7. Release Notes 生成模式(GitHub Release 正文)
|
|
293
294
|
|
|
294
|
-
`CHANGELOG.md` 里的 `## Unreleased`
|
|
295
|
-
|
|
295
|
+
`CHANGELOG.md` 里的 `## Unreleased` 条目是**原始数据**,每条对应一个故事或修复。
|
|
296
|
+
发版时(release script 打 tag 前,或手动 `roll release notes`),把这些散装 bullet 整理成**给人读的 Release 正文**。
|
|
296
297
|
|
|
297
298
|
这是两个不同的产物:
|
|
298
299
|
- `CHANGELOG.md` — 机器写、给 `roll update` 之后展示用,条目可以多
|
|
@@ -300,7 +301,7 @@ After successful deploy in `$roll-build` / `$roll-fix`:
|
|
|
300
301
|
|
|
301
302
|
### 7.1 何时触发
|
|
302
303
|
|
|
303
|
-
-
|
|
304
|
+
- 发版脚本在 commit 之前调用本 skill 并传入 `--release-notes` flag
|
|
304
305
|
- 或用户手动说"帮我整理 Release Notes"
|
|
305
306
|
|
|
306
307
|
### 7.2 分组规则
|
|
@@ -360,13 +361,14 @@ After successful deploy in `$roll-build` / `$roll-fix`:
|
|
|
360
361
|
|
|
361
362
|
## 8. features.md 重写模式(产品 SOT)
|
|
362
363
|
|
|
363
|
-
US-DOC-008 — `
|
|
364
|
-
|
|
365
|
-
|
|
364
|
+
US-DOC-008 — 发版脚本(maintainer-private,位于 `roll-meta/ops/release.sh`)
|
|
365
|
+
在 changelog/release-notes 生成完后会再调一次本 skill,请求"整体重写
|
|
366
|
+
`.roll/features.md`"。这次调用的语义和上面两种完全不同:**不是基于本版
|
|
367
|
+
Story 增量**,而是基于**项目整体当前状态**。
|
|
366
368
|
|
|
367
369
|
### 8.1 何时触发
|
|
368
370
|
|
|
369
|
-
|
|
371
|
+
发版脚本完成 changelog/release-notes 写盘后,喂一段以
|
|
370
372
|
`## 当前任务:重写 .roll/features.md(Section 8)` 开头的 prompt。
|
|
371
373
|
|
|
372
374
|
### 8.2 输入
|
|
@@ -420,9 +422,9 @@ prompt 会包含:
|
|
|
420
422
|
- 该 Feature 下**所有** Story 均为 `📋 Todo` → 在描述末尾追加 `*(规划中)*`
|
|
421
423
|
- 只要有 **≥1 个** `✅ Done` Story → 正常展示,**不加**任何标记
|
|
422
424
|
- 一眼可见:规划中的 Feature 在每个 Epic 分组的末尾列出
|
|
423
|
-
- **FIX-051
|
|
425
|
+
- **FIX-051 兜底**:发版脚本在 AI 重写后会跑机械校验
|
|
424
426
|
`_enforce_planning_markers`,即使本规则被 AI 漏掉也会自动补 `*(规划中)*`;
|
|
425
|
-
|
|
427
|
+
规则的权威实现是发版脚本里的纯 shell 函数,prompt 这条只是软提示
|
|
426
428
|
- 描述写 1 句话 **产品视角**:用户能用它做什么,避免实现细节
|
|
427
429
|
- **语言:单一中文**。Feature 名(如 `roll-loop` / `Cross-Agent Peer Review`)和命令、环境变量等术语保留英文原样;描述句一律中文。**不要**在条目下追加英文翻译行(早期 v517.x 双语混排版式已废弃,理由:扫读困难、维护翻倍、与 `guide/en|zh/` 平行目录约定不一致;英文受众走 site 端 i18n)
|
|
428
430
|
- 分组用 BACKLOG 的 Epic 名,原序,不重排
|
|
@@ -435,5 +437,5 @@ prompt 会包含:
|
|
|
435
437
|
### 8.5 失败安全
|
|
436
438
|
|
|
437
439
|
如果 prompt 信息不足(BACKLOG 解析失败等),**不要部分写入** —— 输出原
|
|
438
|
-
|
|
440
|
+
文件内容即可。发版脚本会捕获 stdout 后比较:内容未变就不 stage。
|
|
439
441
|
|
|
@@ -137,7 +137,7 @@ Parse .roll/backlog.md for all `### Feature: <name>` groups that contain ≥1
|
|
|
137
137
|
| REFACTOR-XXX | features.md 功能目录落后于 BACKLOG,N 个已完成功能区未收录,用户无法通过产品目录发现这些功能 — flagged by dream YYYY-MM-DD | 📋 Todo |
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
The catalog is auto-updated by `
|
|
140
|
+
The catalog is auto-updated by the release script (maintainer-private at `roll-meta/ops/release.sh`) at release time (Section 8 of roll-.changelog). Between releases, this check surfaces the coverage gap so it isn't silently skipped.
|
|
141
141
|
|
|
142
142
|
**REFACTOR entry format for doc findings:**
|
|
143
143
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: roll-deck
|
|
3
|
+
license: MIT
|
|
4
|
+
allowed-tools: "Read, Edit, Write, Glob, Grep, Bash(git:*)"
|
|
5
|
+
description: "Generate a bilingual 18-slide deck.md from a topic. Reads the current project (README, AGENTS.md, backlog, features), discusses outline with the user when needed, and writes one file: .roll/slides/<slug>/deck.md. Each slide carries title_en/title_zh + body_en/body_zh + evidence (file:line). HTML rendering is a separate bash step (roll slides build)."
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# roll-deck
|
|
9
|
+
|
|
10
|
+
> Topic in, deck.md out. AI authoring layer of the slide-deck pipeline.
|
|
11
|
+
> 主题进,deck.md 出。幻灯片管线的 AI 创作层。
|
|
12
|
+
|
|
13
|
+
## Trigger
|
|
14
|
+
|
|
15
|
+
User invokes `roll-deck` from the CLI:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
$roll-deck "Introducing Roll Loop"
|
|
19
|
+
$roll-deck "How TCR keeps us honest"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`roll slides new "<topic>"` shells out to the selected agent with this skill loaded.
|
|
23
|
+
`roll slides new "<topic>"` 会通过所选 agent 加载本 skill。
|
|
24
|
+
|
|
25
|
+
## When Not to Use
|
|
26
|
+
|
|
27
|
+
- Rendering an existing `deck.md` to HTML → use `roll slides build <slug>` (bash, no AI).
|
|
28
|
+
- Listing or previewing decks → use `roll slides list` / `roll slides preview <slug>`.
|
|
29
|
+
- Authoring backlog stories → use `$roll-design` / `$roll-idea`.
|
|
30
|
+
|
|
31
|
+
## Hard Constraints 硬约束
|
|
32
|
+
|
|
33
|
+
- **You may write exactly one file**: `.roll/slides/<slug>/deck.md`.
|
|
34
|
+
- You MUST NOT edit any other file: no README, no AGENTS.md, no backlog, no source code, no `.roll/slides/*.html`.
|
|
35
|
+
- HTML rendering is `roll slides build <slug>` — never produce HTML here.
|
|
36
|
+
- If `.roll/slides/<slug>/deck.md` already exists, ask the user before overwriting.
|
|
37
|
+
|
|
38
|
+
## Inputs
|
|
39
|
+
|
|
40
|
+
- `<topic>`: a free-text topic string (required).
|
|
41
|
+
- `<template>`: template name, default `introduction-v3`.
|
|
42
|
+
- `<slug>`: kebab-case slug derived from the topic by the CLI; you receive it.
|
|
43
|
+
|
|
44
|
+
## Workflow
|
|
45
|
+
|
|
46
|
+
### 1. Read the project 阅读项目
|
|
47
|
+
|
|
48
|
+
Read in this order, skipping files that don't exist:
|
|
49
|
+
|
|
50
|
+
1. `README.md` — top-level pitch and entry points.
|
|
51
|
+
2. `AGENTS.md` — communication style, conventions, where to look.
|
|
52
|
+
3. `.roll/backlog.md` — recently Done Stories and the top of the Todo queue.
|
|
53
|
+
4. `.roll/features/**/*.md` — feature specs relevant to the topic.
|
|
54
|
+
5. Targeted `Grep` for the topic keywords across the repo (≤ 30 hits).
|
|
55
|
+
|
|
56
|
+
Stop reading when you have enough to write 18 slides with concrete evidence.
|
|
57
|
+
拿到足够 18 张 slide 的证据就停。
|
|
58
|
+
|
|
59
|
+
### 2. Outline check outline 校验
|
|
60
|
+
|
|
61
|
+
- If the topic is unambiguous and the project gives clear evidence (high confidence), proceed directly.
|
|
62
|
+
- If the topic is vague (multiple valid framings), restate the proposed 18-slide outline in 3–5 lines and ask the user to confirm or adjust before writing. Wait for confirmation.
|
|
63
|
+
- Never ask more than one round of questions — pick the strongest framing and proceed.
|
|
64
|
+
|
|
65
|
+
### 3. Generate 18 slides 生成 18 张 slide
|
|
66
|
+
|
|
67
|
+
Default count is `18` (or the count the template prescribes). Each slide MUST contain:
|
|
68
|
+
|
|
69
|
+
- `title_en` — short English title (≤ 8 words).
|
|
70
|
+
- `title_zh` — short Chinese title (≤ 16 chars).
|
|
71
|
+
- `body_en` — markdown body, concise, ≤ 200 words.
|
|
72
|
+
- `body_zh` — Chinese body, concise, ≤ 200 chars.
|
|
73
|
+
- `evidence` — list of `<path>:<line>` references that ground the claim.
|
|
74
|
+
|
|
75
|
+
**Grounding threshold**: every 3 consecutive slides MUST contain at least 1 evidence citation. If you cannot ground a slide, label it `⚠️ unverified` in the body and explain why in one line.
|
|
76
|
+
**Grounding 阈值**:每 3 张 slide 至少 1 条 evidence。无法取证时打 `⚠️ unverified` 并一行说明。
|
|
77
|
+
|
|
78
|
+
Bilingual rule: English and Chinese MUST be on separate lines in the rendered body — never inline on the same line.
|
|
79
|
+
|
|
80
|
+
### 4. Write deck.md 写文件
|
|
81
|
+
|
|
82
|
+
Write to `.roll/slides/<slug>/deck.md` with this structure:
|
|
83
|
+
|
|
84
|
+
```markdown
|
|
85
|
+
---
|
|
86
|
+
template: <template>
|
|
87
|
+
slug: <slug>
|
|
88
|
+
title_en: "<topic in English>"
|
|
89
|
+
title_zh: "<topic in Chinese>"
|
|
90
|
+
total_slides: 18
|
|
91
|
+
created: <YYYY-MM-DD>
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Slide 1
|
|
95
|
+
title_en: "..."
|
|
96
|
+
title_zh: "..."
|
|
97
|
+
body_en: |
|
|
98
|
+
...
|
|
99
|
+
body_zh: |
|
|
100
|
+
...
|
|
101
|
+
evidence:
|
|
102
|
+
- README.md:12
|
|
103
|
+
- .roll/backlog.md:34
|
|
104
|
+
|
|
105
|
+
## Slide 2
|
|
106
|
+
...
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`total_slides` MUST match the number of `## Slide N` blocks. The validator (run by `roll slides build`) will reject mismatches.
|
|
110
|
+
|
|
111
|
+
### 5. Prompt the user 提示用户
|
|
112
|
+
|
|
113
|
+
After writing, print a single bilingual block:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
deck.md written → .roll/slides/<slug>/deck.md
|
|
117
|
+
deck.md 已写入 → .roll/slides/<slug>/deck.md
|
|
118
|
+
|
|
119
|
+
Next: roll slides build <slug>
|
|
120
|
+
下一步:roll slides build <slug>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Do not run `roll slides build` yourself — that is a deliberate human gate.
|
|
124
|
+
|
|
125
|
+
## Output format expectations
|
|
126
|
+
|
|
127
|
+
- One file written: `.roll/slides/<slug>/deck.md`.
|
|
128
|
+
- Final agent message ends with the bilingual "Next" hint above.
|
|
129
|
+
- No HTML, no edits to any other file.
|
|
130
|
+
|
|
131
|
+
## Rules
|
|
132
|
+
|
|
133
|
+
- Honour the hard constraints above. The CLI will trust you not to scribble outside the deck file.
|
|
134
|
+
- If `.roll/` does not exist, create it first (with `mkdir -p .roll/slides/<slug>`).
|
|
135
|
+
- If the project has no `README.md` or `AGENTS.md`, proceed using what is available and flag affected slides as `⚠️ unverified`.
|
|
136
|
+
- Keep slides terse — this is a deck, not a doc.
|