Install
openclaw skills install @sedey999/book-to-learn把「一本书 / 一个在线文档站 / 一份资料」拆解成学习卡片。 三种来源模式:书籍(PDF/DOCX/HTML/EPUB/TXT)、网站(sitemap + 导航抓取)、 资料拆卡(把国标 / 手册 / 教程 / 网页拆成「一图一知识点」的竖版卡片长图,一次性出图)。 英文内容自动联网核对术语并实时翻译;中文内容跳过翻译。 卡片类型:A4 标准卡片、大字闪卡、移动端学习长图、中英对照文档、竖版知识卡片长图(3:4/9:16 等)。 支持批量取卡(一次推 N 张)与可选的双语文档归档。 跨平台可用:默认产物落本地目录,IMA / 飞书为可选渠道。
openclaw skills install @sedey999/book-to-learn| 模式 | 来源 | 产出 | 节奏 |
|---|---|---|---|
| A. 书籍 | 一本书(PDF/DOCX/EPUB/HTML/TXT) | A4 卡片 / 大字闪卡 / 学习长图 / 中英对照 | 拆解一次,之后每天推一张 |
| B. 网站 | 一个在线文档站 | 同上 | 同上(增量更新) |
| C. 资料拆卡 | 一份资料(国标 / 手册 / 教程 / 网页) | 竖版知识卡片长图(默认 3:4)+ 全量文本稿 | 一次性全部出图 |
依据用户要的东西选模式:要「跟着学的每日推送」→ A/B;要「一整套可归档的知识卡片」→ C。 C 模式完整规范见下方「模式 C」。
0.(最高优先级)首次使用必须先与用户逐项确认配置,确认前不要开始拆解、更不要推送。
确认清单写在 config.confirmed.items:来源与范围、语言与是否翻译、卡片类型、
推送渠道、命名与编号、文档(操作指南/进度笔记)存放位置、失败通知方式。
逐项确认后把结果写回 config(confirmed.at / confirmed.by / 各项置 true)。
未确认时 push_card.py next 会直接拒绝发载荷——这是刻意设的闸门,不要绕过。
1.(模式 C 同样强制)拆卡模式的三道门禁,任何情况下都不得跳过: ① 先出 1 张示例卡让用户确认样式 → ② 生成全量文本稿让用户逐条确认 → ③ 才批量出图。 理由:样式或文本没定就批量出图 = 全部返工。也不存在「时间紧就跳过」的例外。
OC009);卡片内标记与笔记录入用
「清单绝对序号」(idx,跳过也占号,如 9 / 287)。两轨不要混。batch.listNextN),供用户决定跳过哪几张。python setup_verify.py(缺依赖/缺字体/缺光栅化通道都会明确提示)。本 skill 面向多平台(Claude Code / CodeBuddy / OpenClaw / 各类 agent 沙箱 / 本地机器)。 除「选用的推送渠道」外,任何环节都不应依赖某个特定平台。
标点符号规范/):
<工作目录>/<任务名>/
GUIDE.md 操作手册(读这一份就能接手,内嵌全部规则与踩坑)
PROGRESS.md 进度(只增不删,底部最新)
cards.json 唯一事实源
src/ 原文提取(full_text.txt 等)
refs/ 参考图 / 标准图(可选)
out/ PNG 产物 + _check.json + _contact_sheet.png
<skill目录>/books/<slug>/(文档在其下 docs/)。
若本平台的 skill 目录不是持久层(容器/沙箱里常会被重置),把数据根指到工作目录:
export B2L_DATA_DIR="$PWD/b2l-data"
# book_setup.py / push_card.py / docs_layer.py / fetch_site.py / validate.py 统一读取该变量
docs_layer.py mirror)。
「持久化目录为底」的原因:平台侧一旦没有删除/编辑接口,唯一的进度记录就被锁在一个地方了。os.path.dirname(os.path.abspath(__file__)) 找自己,
不依赖任何固定安装路径。下文 $SD 代表本 skill 目录。references/platform-notes.md。pip3 install weasyprint pillow numpy pymupdf
pip3 install python-docx beautifulsoup4 pypdf pdfminer.six ebooklib striprtf # 书籍多格式提取
# weasyprint 出 PDF;numpy 用于拆卡引擎的内容高度测量;pymupdf 做 PDF→PNG(纯 wheel,无需系统库)
# Windows/macOS 若 pip 装到用户目录,用 python3 -m pip install --user …
体检:python3 setup_verify.py(会分别报「渲染核心 / 光栅化通道 / CJK 字体 / 拆卡引擎」四块)。
apt-get install -y fonts-noto-cjk(没有中文字体会渲染成方块)。cd /tmp && curl -sL -o ima-skills.zip "https://app-dl.ima.qq.com/skills/ima-skills-1.1.7.zip"
mkdir -p ima-skills-extracted && unzip -o ima-skills.zip -d ima-skills-extracted >/dev/null 2>&1
cp -r ima-skills-extracted/ima-skill ~/.codebuddy/skills/ima-skill # 或 ~/.openclaw / ~/.claude / ~/.agents 下
upload_ima.py会自动在~/.codebuddy、~/.openclaw、~/.claude、~/.copilot、~/.agents等路径下查找 ima-skill,也可用环境变量IMA_SKILL_DIR显式指定。API Key 获取:https://ima.qq.com/agent-interface 凭据一律走环境变量(IMA_OPENAPI_CLIENTID/IMA_OPENAPI_APIKEY),不要写进 config 文件。 不用 IMA 的话,本节整节跳过。
部分沙箱里 node 可能被 bun shim 劫持。所有 node 调用用 /usr/bin/node 并清除 NODE_OPTIONS。upload_ima.py 已内置。
SKILL_DIR:本 skill 所在目录(各平台路径不同)。脚本内已自动定位,无需手动指定。下面
$SD代表 SKILL_DIR。
cd $SD && python3 book_setup.py extract <书文件路径> --slug <book-slug>
slug = 书的 URL 友好标识(如 designing-data-intensive-apps)。输出 books/<slug>/full_text.txt。
cd $SD && python3 book_setup.py init <book-slug> --title "书名" --lang <zh|en> --granularity <chapter|section|topic>
生成 books/<slug>/config.json 骨架,然后与用户确认并填写:
language:zh(中文书,无翻译)/ en(英文书,需翻译)pushMethod:local(默认,只落本地)/ ima / feishu(webhook)/ feishu-api(飞书 Open API,支持图片/文件直发)ima.kbName / ima.folderName:仅 pushMethod=ima 时填(用 list-kb 命令列知识库让用户选)feishu.webhook / feishuApi.*:仅选飞书时填notifyWebhook:失败通知 webhook(必填,任何失败都发此通知)向用户说明推送方案并让其选择:
读取 full_text.txt(大书用 offset/limit 分段读,先读前 8000 字符识别标题/作者/章节/目录)。
按 config.granularity 拆解为知识点,输出大纲 JSON 供用户确认:
[{"id":"ch01-01","chapter":"第一章","topic":"主题"}]
必须等用户确认或调整大纲后,再进入 Step 4。
为每个知识点生成完整对象(读取 full_text.txt 对应章节内容),写入 books/<slug>/items.json:
{
"id": "ch01-01", "chapter": "所属章节", "topic": "知识点主题",
"coreIdea": "核心观点(原文语言)",
"explanation": "详细解释(原文语言,含 markdown 链接 [text](url))",
"quote": "金句(若有)", "application": "应用场景(若有)",
"image": "原书配图链接(若有)",
"relatedLinks": [{"href":"url","text":"标题"}],
"terminology": ["核心术语"], "link": "来源链接"
}
items.json 是唯一事实源:推送载荷直接读它的字段;cards/*.html 只作人工预览。
cd $SD && python3 book_setup.py gen-cards <book-slug> # 生成卡片预览
cd $SD && python3 book_setup.py gen-index <book-slug> # 生成索引(同时初始化 progress.json)
cd $SD && python3 book_setup.py download-imgs <book-slug> # 下载配图并内嵌 base64
cd $SD && python3 book_setup.py prompt <book-slug> # 输出定时任务提示词
python fetch_site.py --base-url https://docs.example.com --slug mydocs \
--include /start --exclude / /start/hubs --limit 300
<url>.md;配图扁平化落盘。sections 统计,与用户确认章节范围与卡片总数,再生成内容源。--urls-file),保持已有 idx 不变。references/site-mode.md。适用:一份资料(国标 / 手册 / 教程 / 规范 / 长网页)→ 「一图一知识点」的竖版卡片长图, 一次性拆分、批量出图、归档。不适用:需要按日分批跟学的书籍(那用模式 A)。
# 在工作目录下,用任务名建文件夹
python3 $SD/tile_setup.py init --dir "<工作目录>/标点符号规范" \
--title "标点符号规范" --source "GB/T 15834-2011《标点符号用法》(docx)" --count 47
# 产物:GUIDE.md(手册)/ PROGRESS.md(进度)/ cards.json(骨架)/ src/ / refs/ / out/
| # | 动作 | 通过的标志 |
|---|---|---|
| 1 | 提取原文 → src/full_text.txt(逐字提取,不要摘要;段落/表格都要) | 全文可查 |
| 2 | 列知识点清单(编号 + 标题 + 要点 + 例子)+ 预计张数 | 用户确认范围与张数 |
| 3 | 只出第 1 张示例卡 | 用户确认样式(版式/配色/字号/信息密度) |
| 4 | tile_setup.py export-md 生成 TEXT.md(全量文本稿) | 用户逐条确认文本(错别字 / 误正对比 / 例子遗漏 / 少见用法单独立卡) |
| 5 | 批量出图 → 逐张复核 → 归档 | 像素校验全通过 |
SD=<skill目录>; D="<工作目录>/标点符号规范"
python3 $SD/tile_setup.py export-md --dir "$D" # cards.json → TEXT.md(文本稿)
python3 $SD/gen_card_tile.py --cards "$D/cards.json" --outdir "$D/out" # 批量出图(默认 3:4)
python3 $SD/gen_card_tile.py --cards "$D/cards.json" --outdir "$D/out" --only c07 # 只出某张(示例卡)
python3 $SD/gen_card_tile.py --cards "$D/cards.json" --outdir "$D/out" --ratio 9:16
python3 $SD/tile_setup.py check --dir "$D" # 复检既有产物
python3 $SD/tile_setup.py sheet --dir "$D" --cols 6 # 拼缩略总览图
python3 $SD/tile_setup.py log --dir "$D" --entry "已出图 47 张。" # 追加进度
python3 $SD/tile_setup.py status --dir "$D"
# 或走统一调度器:python3 $SD/render.py --type tile-3x4 --cards "$D/cards.json" --outdir "$D/out"
顶层:group(卡组名,显示在角标)/ footer(页脚来源文字)/ ratio(默认 3:4)/ cards[]。
每张卡:id(内部标识)/ index(序号,决定文件名前缀 NN_ 与角标)/ filename(文件名主体)/ title / subtitle / accent(配色 key)/ blocks[]。
blocks 六种类型(tag 是左侧标签文字,color 是标签配色):
| type | 用途 | 字段 |
|---|---|---|
text | 核心用法 / 概述 | text |
list | 要点 / 其他用法 / 注意事项 | items[](支持 \n 换行) |
compare | 易错对比(误 / 正) | pairs[{wrong,right}] |
marked | 符号示意(字下加标记) | text + mark:"dot"(着重号)/ "line"(专名号) |
emphasis | 一句话记忆点 | text |
diagram | 字格 / 位置示意 | rows[{label, note?, cells[]}] |
diagram 的 cell(图元)可选形态:
{"base":"字","mark":"。","where":"br"}(字 + 位置标记:bl/br/bc/c/l/r/top)、
{"t":"字"}(灰字占位)、{"c":"︱","pos":"c"}(红色符号)、{"w":0.5|2}(半宽格 / 双宽格)、
{"plain":true}(去虚线框)、{"vbar":true}(竖线)、{"pair":["甲","乙"]}(同格两字)、
{"vtext":"﹃\n字\n﹄"}(同格竖排文本,\n 换行)、{"glyph":"字","side":"right","plain":true}(字右侧着重圆点)、
{"base":"字","lmark":"︳"}(同格内左侧内嵌专名号 / ︴ 为浪线式书名号)。
改内容只改 cards.json;删除/拆卡/重排一律写
transform*.py脚本(幂等、可复跑), 不要手改 JSON(易引入语法错误,也无法复跑)。
--ratio):3:4(默认)|9:16|1:1|4:3|16:9;@192dpi 输出 = CSS 尺寸 ×2。第 index / total 张 · group。误 / 正、[OK]。out/_check.json;失败即非 0 退出,不要交付。
注意:页脚判定必须看页脚底色带,不能用「底部有没有文字像素」——溢出时正文文字也会 落在页 1 底部,会造成假通过(实测 5 页 PDF 曾被判通过)。颜色容差 ≤3。
tile_setup.py check --dir "$D";总览:tile_setup.py sheet --dir "$D"。_check.json 的 ink_bbox、footer_ink_ratio),必要时把
局部裁开再看。听视觉模型的判断前先问:像素支持它吗?flex: 0 0 auto(禁止压缩),否则长内容会把相邻元素挤出边界、造成重叠。<span> 画线宽度可能失效 → 改用 border-left 或显式 width/height。min-width: 0 无效(拿它做 flex 收缩保护不管用)→ 用固定比例(如 flex: 0 0 60%)。unicode-range 的破折号兜底会被静默忽略(渲染公共库已自动收敛为短栈)。.gl-vdot 要用 flex: 0 0 auto + 固定宽高,否则会被 flex 拉伸成椭圆。out/*.png + 文本稿一起放进去。【旧版N-可删除】… 腾出干净名,再传新版,
并明确告知用户需要手动清理哪些旧文件(API 删不掉)。|、反斜杠)写入被网关拦截时:改走「写文件 → 对象存储/上传接口 → 引用 key」的路径。--force,让脚本自身的当日/周末/锁守卫生效,防重复推送):
cd $SD && python3 push_card.py next --book <slug> > <临时目录>/b2l_payload.json
(Windows 无 /tmp/,用 %TEMP%)。normalize_quotes.py 会自动修正直引号)。b2l_zh.json(topicZh/coreIdeaZh/explanationZh/quoteZh/applicationZh/terminologyZh/relatedLinksZh/note)。python3 render.py --type <cardType> --payload <payload> --zh <zh> --out <产物路径> --language enconfig.pushMethod):
local:产物留在工作目录即可(无需推送)。ima:python3 upload_ima.py --file "<pdf>" --config books/<slug>/config.json --book-dir books/<slug>
退出码 0=成功;2=密钥失效(已发通知)不计进度结束;1=其他错误不更新进度结束。feishu:python3 send_feishu.py --payload … --zh … --config … --language enfeishu-api:python3 send_feishu_api.py --payload … --zh … --config … --language en
(如需同时发图片卡片,追加 --image <png> --config …)python3 push_card.py mark --book <slug> <nextId> success。同上,但跳过步骤 2-4,--language zh,不传 --zh。
python3 push_card.py list-next --book <slug> --n 10 # 预览(不加锁、不改状态)
python3 push_card.py next --book <slug> --n 5 # 取 5 张载荷
python3 push_card.py mark --book <slug> --ids id1,id2,id3,id4,id5 success
--n 1(默认)保持「每日一张 + 当日锁 + 周末守卫」语义,适合定时任务。--n > 1 是显式用户意图:跳过当日/周末守卫与单卡锁,取完必须用 --ids 一次性回写。render.py)python render.py --list # 看类型→引擎映射
python render.py --type long-study --payload p.json --zh z.json --out card.png
python render.py --type tile-3x4 --cards cards.json --outdir out # 拆卡引擎
| cardType | 引擎 | 产物 |
|---|---|---|
pdf-standard | gen_card_pdf.py | A4 标准卡片 PDF |
pdf-large | gen_card_pdf_large.py | 大字闪卡 PDF |
long-image | gen_image.py | 闪卡配图 PNG |
long-study | gen_card_long.py | 移动端学习长图 PNG |
pdf-bilingual | gen_card_bilingual.py | 中英对照文档 PDF |
tile-3x4 / tile-9x16 / tile-1x1 / tile-4x3 / tile-16x9 | gen_card_tile.py | 竖版知识卡片长图(拆卡模式) |
feishu-card / feishu-card+image | 推送层(见下方飞书方案) | — |
加自定义引擎不用改代码:在 config.json 里写
"renderer": {"my-type": "my_engine.py"}。 版式规范见references/design-spec.md。
# 模式 A/B
python docs_layer.py init --slug <slug> --title "…"
python docs_layer.py refresh --slug <slug>
python docs_layer.py log --slug <slug> --entry "…"
python docs_layer.py status --slug <slug>
python docs_layer.py mirror --slug <slug> # 可选:镜像到 IMA 笔记
# 模式 C
python tile_setup.py init|export-md|sheet|check|log|status --dir "<任务目录>"
本地为底,平台镜像可选。规范见 references/workflow-notes.md。
GUIDE.md(模式 C)或 docs/guide.md(模式 A/B)——恢复规则;push_card.py status --book <slug> / tile_setup.py status --dir <D>——定位进度;会话记忆被压缩后的恢复顺序:
GUIDE.md→PROGRESS.md底部 →cards.json/progress.json→ 本文件 +references/。
| 脚本 | 作用 |
|---|---|
setup_verify.py | 开工前先跑:依赖 / 光栅化通道 / 中文字体 / 破折号字体 / 拆卡引擎体检 |
render_common.py | 渲染纯工具(字体探测、破折号兜底、MDX 清理、转义、表格、配图、裁白、光栅化) |
render.py | 按「卡片类型 → 引擎」调度(含 tile-* 拆卡类型) |
gen_card_tile.py | 拆卡引擎(3:4 默认 / 9:16 / 1:1 / 4:3 / 16:9;自适应防裁切;内置像素校验) |
tile_setup.py | 拆卡编排(init / export-md / sheet / check / log / status;手册与进度写在工作目录) |
gen_card_pdf.py / gen_card_pdf_large.py | A4 标准 / 大字卡片引擎 |
gen_card_long.py | 学习长图引擎(800px,金句→术语→观点→要点) |
gen_card_bilingual.py | 中英对照文档引擎(逐块对照) |
gen_image.py | 闪卡配图引擎(1:1 / 1:4) |
fetch_site.py | 网站模式抓取(sitemap + 导航双通道 → catalog.json + raw/) |
book_setup.py | 书籍拆解编排(extract / init / gen-cards / gen-index / download-imgs / prompt) |
items_io.py | 内容源读写(兼容 items.json 与分章节 items/ 两种布局) |
push_card.py | 进度与取卡(status / list-next / next / mark / weekday / list-books) |
validate.py | 推送前校验:内容源一致性 + 中英逐块对齐 |
docs_layer.py | 操作指南 + 进度笔记(本地为底,IMA 可选镜像) |
upload_ima.py / send_feishu.py / send_feishu_api.py | 可选推送渠道 |
process_attachments.py | 相关链接附件下载/转换/重命名 |
notify_failure.py | 通用失败通知(参数化 webhook) |
tests/ | 渲染公共库自检 + 离线测试数据 |
数据目录:books/<slug>/(模式 A/B)或 <工作目录>/<任务名>/(模式 C)。
| 文件 | 内容 |
|---|---|
references/config-schema.md | 配置项逐项说明 + 首次启动确认清单 |
references/site-mode.md | 网站模式完整流程 |
references/design-spec.md | 卡片版式规范(含拆卡 3:4 版式) |
references/pitfalls.md | 踩坑清单(渲染/字体/流程/质量,跨平台表述) |
references/workflow-notes.md | 操作指南 + 进度笔记规范,记忆压缩后的恢复路径 |
references/platform-notes.md | 平台适配注意事项(含 IMA 环境:凭据、上传、无删除接口等) |
注意:关键规则与踩坑已内嵌在本文件与
GUIDE.md里,不依赖「去翻参考文档」; references 是加深与排障用的补充材料。
prompt 对默认 pushMethod=local 现在正确生成本地落盘指令(原先错误生成飞书指令,
默认配置每日推送必失败);mark 增加 id 白名单校验(误传文件名不再静默重置整本书进度);
lastPushedId 不在 index.json 时报 stale_progress 而非从头重推;sitemap 索引
(sitemapindex)正确展开子 sitemap(原先把子 sitemap 地址当页面 URL,真实页面全漏);
normalize_quotes 代码块/行内代码/URL 内的直引号受掩码保护(原先 print("你好")
会被改成弯引号),smart 模式成为默认;fetch_site.py 增量更新(--urls-file)不再
覆写毁掉 catalog.json(按 URL 合并、保旧 idx);upload_ima 子进程异常不再把 COS
凭据带进 traceback。中危:--force 现在可接管失败锁;批量 mark 按 index 顺序
回写 lastPushedId;gen_card_long 支持 items.json 标准的字符串列表术语(中文学术语区
不再消失);progress/items/catalog 等 JSON 全部原子写;split 布局统一走 items_io
(gen-cards/gen-index/download-imgs/log-progress 不再绕过);prompt 生成校验步骤并
传播 B2L_DATA_DIR;book_setup/gen_card_bilingual 等文档与 CLI 参数对齐(slug 两种
写法都接受);JSON 文件统一 utf-8 显式编码;飞书 webhook 不再发送 URL 型 img 元素
(整卡被拒),Open API 版改为下载后上传拿 image_key;process_attachments 的 curl
退出码纳入判定、同名附件加短哈希防覆盖。低危:gen_image 固定比例校验页数、
PDF 产物魔数校验、HTML GB18030 回退解码、file:// 百分号编码、单引号属性解析、
fitz 同名垃圾包防护、时区支持(B2L_TZ)、死代码清理等。gen_card_tile.py(多比例竖版卡片长图,
自适应防裁切,内置像素校验)、tile_setup.py(工作目录内的任务编排 + 内嵌经验的
操作手册/进度)。跨平台改造:默认落本地、IMA/飞书降为可选渠道、文档一律落工作目录、
新增 references/platform-notes.md;踩坑清单并入拆卡实战经验(测量阈值含页脚、
flex: 0 0 auto、weasyprint min-width:0 无效、视觉模型误判以像素为准、字体家族 ≤4、
着重号圆点防拉伸等)。复核定稿:拆卡校验改为双硬判据(PDF 页数必须为 1 +
底部页脚底色带存在),修掉「内容溢出却校验假通过」的漏洞;默认 pushMethod 统一为
local;A/B 模式数据根支持环境变量 B2L_DATA_DIR 重定向到持久化目录。构造飞书 interactive 卡片 JSON,POST 到 webhook URL。
App ID + App Secret 直发指定会话,支持原生图片/文件。
{"pushMethod": "feishu-api",
"feishuApi": {"appId": "cli_xxx", "appSecret": "xxx", "chatId": "oc_xxx"}}
也可用环境变量 FEISHU_APP_ID / FEISHU_APP_SECRET / FEISHU_CHAT_ID。
创建应用:https://open.feishu.cn/app → 开通 im:message / im:message:send_as_bot / im:resource → 机器人加入目标群。
发送模式:--payload [--zh](卡片)/ --image <png>(图片)/ --file <pdf>(文件)。
每本书在 config.json 的 template 字段选择模板(首次配置时 AI 引导用户选)。
| 模板 ID | 名称 | 脚本 | 适用场景 |
|---|---|---|---|
pdf-standard | PDF 标准卡片 | gen_card_pdf.py | 工具书、长文知识点 |
pdf-large | PDF 大字卡片 | gen_card_pdf_large.py | 单词、术语、短知识点 |
feishu-card | 飞书交互卡片 | send_feishu.py | 即时学习提醒 |
feishu-card+image | 飞书卡片+图片 | send_feishu.py + gen_image.py | 知识点可视化 |
配色(Material 色系,所有模板统一):绿 #1e8e3e 核心观点 / 蓝 #1a73e8 解释·标题栏 /
紫 #7b1fa2 金句 / 橙 #f9ab00 应用·术语 / 红 #d93025 术语标签·错误状态。
字体栈:跨平台(雅黑 → 苹方 → 冬青黑 → Noto CJK → 思源黑体 → 文泉驿 → 宋体),
渲染前由 render_common.normalize_font_stack() 自动收敛为短栈(见踩坑第 12 条)。
超链接:一律纯文字(多数知识库/IM 不能点击)。
python3 book_setup.py summary <slug> 输出详细配置(书名/语言/粒度/卡片转化/
模板/通道/目标/通知/文件清单),用户确认后再生成定时任务提示词。books/<slug>/daily-progress.md,每次推送成功后追加一行:
| 日期 | 序号 | 卡片ID | 主题 | 推送方式 | 状态 |
|------|------|--------|------|----------|------|
| 2026-06-30 | 1/118 | ch01-01 | 西方音乐记谱法导论 | local | 成功 |
cd $SD && python3 push_card.py list-bookspython3 push_card.py status --book <slug> | python3 tile_setup.py status --dir <D>python3 book_setup.py summary <slug>--force):python3 push_card.py next --book <slug> --forcepython3 book_setup.py prompt <slug>python3 book_setup.py log-progress <slug> --card-id <id>progress.json;失败绝不计进度,确保下次重推同一张。YYYY-MM-DD。normalize_quotes.py 会把英文直引号自动修正为中文弯引号。