关于 publish-substack-article
publish-substack-article 是将 Markdown 文章整理为富文本并保存到 Substack 草稿的 Skill,供希望通过浏览器自动化准备文章的人使用。它支持 Playwright MCP 或 Chrome DevTools MCP,并将表格转为图片处理;草稿需由用户检查并手动发布。
主要功能
- 01从 Markdown 提取标题与正文
- 02转换为 HTML 富文本并粘贴
- 03将表格转换为图片后插入
- 04自动保存并核验草稿状态
- 05支持 Playwright MCP 与 Chrome DevTools MCP
适用场景
- 01将 Markdown 文章存为 Substack 草稿
- 02保留标题、代码块与链接等格式
- 03在文章中以图片呈现表格
- 04发布前检查草稿并手动确认
MCP 相关
使用 Playwright MCP,或在其不可用时使用 Chrome DevTools MCP,控制浏览器导航、编辑器输入和图片上传。它将文章保存为草稿,不会启动发布流程。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sugarforever/ 01coder-agent-skills --skill publish-substack-article安装后在 Claude Code 中输入/publish-substack-article确认已加载。
详细介绍
发布 Substack 文章
将 Markdown 内容发布到 Substack 文章编辑器,并将 Markdown 转换为 HTML 后以富文本形式粘贴。保存为草稿,供用户审核后再发布。
先决条件
- 浏览器自动化 MCP(二选一):
- Chrome DevTools MCP(
mcp__chrome-devtools__*) - Playwright MCP(
mcp__playwright__*)
- Chrome DevTools MCP(
- 用户已登录 Substack
- Python 3 和
markdown包(pip install markdown) copy_to_clipboard.py脚本(与 publish-zsxq-article 技能共享)
浏览器 MCP 工具映射
此技能同时适用于 Chrome DevTools MCP 和 Playwright MCP:
| 操作 | Chrome DevTools MCP | Playwright MCP |
|---|---|---|
| 导航 | navigate_page | browser_navigate |
| 获取快照 | take_snapshot | browser_snapshot |
| 截取屏幕截图 | take_screenshot | browser_take_screenshot |
| 点击元素 | click | browser_click |
| 填写文本 | fill | browser_type |
| 上传文件 | upload_file | browser_file_upload |
| 按键 | press_key | browser_press_key |
| 执行 JS | evaluate_script | browser_evaluate |
优先级:默认使用 Playwright MCP。仅在 Playwright MCP 不可用时使用 Chrome DevTools MCP。
检测:运行时优先使用 mcp__playwright__browser_navigate。只有在 Playwright 工具不可用时,才回退使用 mcp__chrome-devtools__navigate_page。
重要 URL
- Substack 控制面板:
https://{publication}.substack.com/publish - 文章编辑器:
https://{publication}.substack.com/publish/post/{postId}
出版物解析
导航到控制面板需要 Substack 出版物的子域名(例如 verysmallwoods.substack.com 中的 verysmallwoods)。不要硬编码默认值。
开始任何导航前,请按以下顺序解析出版物:
- 技能参数 — 如果用户调用技能时提供了出版物子域名,请使用该值。
- 环境变量 — 检查
SUBSTACK_PUBLICATION(可选,适用于反复向同一出版物发布的用户)。 - 询问用户(如果以上信息都没有):
请提供 Substack publication subdomain(例如 verysmallwoods.substack.com 里的
verysmallwoods)。
在解析出出版物子域名之前,不要继续执行步骤 3(导航)。
编辑器界面
Substack 文章编辑器使用 Tiptap(基于 ProseMirror 的所见即所得编辑器)。
关键元素
- 标题输入框:
textbox "title"(占位符:"Title") - 副标题输入框:
textbox "Add a subtitle…" - 内容区域:
.ProseMirror(Tiptap 编辑器,"Start writing...") - 保存状态:
button "Saved"(自动保存) - 预览按钮:
button "Preview" - 继续按钮:
button "Continue"(发布流程 — 请勿使用) - 设置侧边栏:
button "Settings"(标题、描述、缩略图)
设置侧边栏(左侧面板)
打开 "Settings" 或 "File Settings" 时:
- 标题:
textbox "Add a title..." - 描述:
textbox "Add a description..." - 缩略图:上传按钮(宽高比为 3:2)
工具栏
粗体、斜体、删除线、代码、链接、图像、音频、视频、引用、列表(项目符号/有序)、按钮、更多(代码块、分隔线、脚注、LaTeX 等)
内容插入方法
重要:使用剪贴板粘贴 HTML 内容,而不是直接填写或粘贴纯 Markdown。
Tiptap 编辑器可原生处理 HTML 粘贴,并将其渲染为富文本内容。工作流程如下:
- 使用 Python 的
markdown库将 Markdown 转换为 HTML - 使用
copy_to_clipboard.py html将 HTML 复制到系统剪贴板 - 聚焦编辑器内容区域
- 按 Cmd+V(macOS)或 Ctrl+V(Windows/Linux)粘贴
为什么要粘贴 HTML?
fill工具 → 内容会被视为纯文本,不带格式- 粘贴纯 Markdown → Tiptap 不会在粘贴时解析 Markdown
- 粘贴 HTML → Tiptap 会将 HTML 渲染为富文本(标题、代码块、链接、粗体等)
已知限制:Substack 编辑器不支持 HTML 表格。表格会被合并为纯文本。有关将表格转换为图像的方法,请参见步骤 0:预处理。
主要工作流
步骤 0:预处理 — 将表格转换为图片
Substack 不会渲染 HTML 表格。它们会变成纯文本。任何 Markdown 表格都必须转换为 PNG 图片并单独上传。
工作流:
-
检测 Markdown 文件中的表格(使用
|构成表格结构的行) -
使用图表转图片技能将每个表格转换为 PNG:
# Extract table to temp file
cat > /tmp/table1.md << 'TABLE_EOF'
| Column 1 | Column 2 | Column 3 |
|----------|----------|----------|
| Data 1 | Data 2 | Data 3 |
TABLE_EOF
# Convert via diagramless.xyz API (auto-detects as table)
node ~/.claude/skills/diagram-to-image/scripts/diagram-to-image.mjs /tmp/table1.md -o /tmp/table1.png
-
记录每个表格在文章中的位置,以便稍后插入(位于哪个标题或段落之后)
-
从内容中移除 Markdown 表格,再进行 HTML 转换(这样粘贴的内容中就不会出现纯文本表格)
图片上传将在粘贴主要内容后进行 — 参见步骤 7。
步骤 1:准备内容
读取 Markdown 文件并提取:
- 标题:取自 YAML frontmatter 的
title字段、H1 标题# Title,或文件名 - 副标题:取自 YAML frontmatter 的
excerpt或description字段 - 内容:完整的 Markdown 正文(去除 YAML frontmatter 和所有交叉引用链接)
步骤 2:将 Markdown 转换为 HTML
使用 Python 的 markdown 库及 tables 和 fenced_code 扩展:
import markdown
import re
with open('/path/to/article.md', 'r') as f:
content = f.read()
# Strip YAML frontmatter
content = re.sub(r'^---\n.*?\n---\n', '', content, flags=re.DOTALL)
# Strip cross-reference links (e.g., English version link)
# Adjust pattern as needed for your articles
content = re.sub(r'^> .* available at.*\n\n?', '', content, flags=re.MULTILINE)
# Convert to HTML
html = markdown.markdown(content, extensions=['tables', 'fenced_code'])
# Write to temp file
with open('/tmp/substack_article.html', 'w') as f:
f.write(html)
重要:不要使用 nl2br 扩展 — 它会将单个换行符转换为 <br> 标签,导致编辑器中出现多余的换行。
步骤 3:导航至 Substack
导航至 Substack 控制面板并新建文章:
# Navigate to Substack dashboard
navigate to: https://{publication}.substack.com/publish
如果尚未登录,请提示用户登录:
请先登录 Substack,登录完成后告诉我。
Please log in to Substack first, then let me know.
步骤 4:新建文章
在控制面板中创建一篇新的文本文章:
- 点击侧边栏中的“Create new”
- 选择“Text post”(或直接导航至新文章 URL)
或者,如果编辑器已打开且文章为空,可直接继续。
步骤 5:填写标题和副标题
- 点击标题文本框(
textbox "title") - 输入文章标题
- 点击副标题文本框(
textbox "Add a subtitle…") - 输入副标题或摘要
click: title textbox
fill/type: article title
click: subtitle textbox
fill/type: article subtitle
步骤 6:插入 HTML 内容(通过剪贴板粘贴)
关键:不要使用 fill 工具 — 它只会插入纯文本,不带格式。
- 将 HTML 复制到系统剪贴板:
python3 /path/to/copy_to_clipboard.py html --file /tmp/substack_article.html
-
点击编辑器内容区域(
.ProseMirror或其中的段落元素) -
按 Cmd+V 粘贴:
press_key: Meta+v (macOS)
press_key: Control+v (Windows/Linux)
这会触发 Tiptap 的 HTML 粘贴处理程序,将内容渲染为格式正确的富文本。
步骤 7:插入表格图片
如果文章中的表格已在步骤 0 转换为图片,请现在插入:
-
导航至编辑器中的正确位置 — 点击表格应出现的段落或空行(相关标题或文本之后)
-
点击图片工具栏按钮(
button "Image")— 会出现包含以下选项的下拉菜单:Image、Gallery、Stock photos、Generate image -
点击下拉菜单中的“Image”菜单项 — 随即打开文件选择对话框
-
通过文件选择器上传图片:
- Playwright MCP:使用
browser_file_upload和图片路径 - Chrome DevTools MCP:使用
upload_file和图片路径
- Playwright MCP:使用
重要说明:
- 文件路径限制:Playwright MCP 只允许从允许的根目录(项目目录)上传文件。如果图片位于
/tmp/,请先将其复制到项目目录 - 对每个表格重复操作:将光标放在正确位置,然后上传每张表格图片
- 删除残留文本:如果表格内容以纯文本形式粘贴(因为预处理时未将其移除),请选中并删除(连按三次段落以选中),然后再插入图片
步骤 8:检查草稿
粘贴后:
- 检查“Saved”状态指示器(绿点和“Saved”文字)
- 截取快照以确认内容结构
- 也可以截屏进行视觉检查
编辑器会自动保存,因此无需显式保存。
步骤 9:报告完成情况
草稿已保存到 Substack。请在 Substack 中预览并手动发布。
Draft saved to Substack. Please preview and publish manually.
Post URL: https://{publication}.substack.com/publish/post/{postId}
完整示例流程
用户:“将 /path/to/my-article.md 发布到 Substack”
0. Pre-process tables (if any)
- Detect Markdown tables
- Create styled HTML for each table
- Render to screenshots (open in browser, screenshot, close tab)
- Remove table Markdown from content
- Note insertion positions
1. Read /path/to/my-article.md
- Extract title from frontmatter or H1
- Extract subtitle from frontmatter excerpt
- Get full Markdown content (with tables removed)
2. Convert Markdown to HTML
- Strip frontmatter
- Use markdown.markdown() with ['tables', 'fenced_code']
- Write to /tmp/substack_article.html
3. Navigate to Substack dashboard or new post
4. Check if logged in
- If not, prompt user to login
5. Fill title and subtitle
6. Copy HTML to clipboard + Paste
- python3 copy_to_clipboard.py html --file /tmp/substack_article.html
- Click editor content area
- Press Cmd+V
7. Insert table images at correct positions
- For each table: click position → Image button → Image menuitem → file upload
8. Verify draft saved
- Check "Saved" status
9. Report success
- "草稿已保存,请手动预览并发布"
关键规则
- 绝不要点击“Continue” — 这会启动发布流程。只保存为草稿(自动保存会处理)
- 始终先转换为 HTML — Tiptap 编辑器无法解析纯 Markdown
- 使用剪贴板粘贴 — 这是插入格式化内容唯一可靠的方式
- 检查登录状态 — 如有需要,提示用户登录
- 保留原始文件 — 绝不修改源 Markdown 文件
- 报告完成情况 — 告知用户草稿已保存,需要手动审核
- 不要使用
nl2br扩展 — 会导致双倍换行 - 表格 → 图片 — 粘贴内容前预处理表格;粘贴后上传图片
- Playwright 文件路径 — Playwright MCP 限制了文件上传的允许根目录;上传前将临时文件复制到项目目录
- 先解析出版物 — 绝不要硬编码出版物子域名。导航前通过技能参数、
SUBSTACK_PUBLICATION环境变量或提示用户来解析(参见“出版物解析”) - 优先使用 Playwright MCP — 默认使用 Playwright MCP;只有在 Playwright 不可用时才使用 Chrome DevTools MCP
故障排除
内容显示为纯文本(没有格式)
如果看到原始 HTML 标签或未格式化的文本:
- 原因:使用
fill工具插入内容,而不是通过剪贴板粘贴 - 解决方案:使用
copy_to_clipboard.py+ Cmd+V 方法(参见步骤 6)
表格未渲染(显示为纯文本)
Substack 的 Tiptap 编辑器不支持 HTML 表格。表格会折叠成行内纯文本。
- 解决方案:通过 diagram-to-image 技能将表格转换为 PNG → 以图片形式上传(参见步骤 0 和步骤 7)
- 替代方案:将简单表格重组为格式化列表
- 如果已粘贴纯文本:三击纯文本段落以选中它,按 Backspace 删除,然后在该位置插入表格图片
需要登录
如果页面显示登录提示:
请先登录 Substack: https://{publication}.substack.com
登录完成后告诉我。
编辑器未加载
如果编辑器元素不可见:
- 等待页面完全加载
- 获取新的快照
- 如果仍未加载,请刷新页面
复制剪贴板失败
如果 copy_to_clipboard.py 失败:
- 确保已安装依赖项:
pip install pyobjc-framework-Cocoa(macOS) - 检查 HTML 文件是否存在且可读
- 先尝试复制较小的测试字符串
元素参考
| 元素 | 选择器/标识符 | 描述 |
|---|---|---|
| 标题输入框 | textbox "title" | 帖子标题 |
| 副标题输入框 | textbox "Add a subtitle…" | 帖子副标题 |
| 内容区域 | .ProseMirror(Tiptap 编辑器) | 帖子内容 |
| 保存状态 | button "Saved" | 自动保存指示器 |
| 预览按钮 | button "Preview" | 预览帖子 |
| 继续按钮 | button "Continue" | 不要使用 — 会启动发布流程 |
| 设置按钮 | button "Settings" | 打开设置侧边栏 |
| 退出按钮 | button "Exit" | 退出编辑器 |
| 图片按钮 | button "Image" | 打开图片上传下拉菜单 |
| 图片菜单项 | menuitem "Image" | 打开图片上传文件选择器 |
| 作者按钮 | button "{PublicationName}" | 作者/出版物选择器 |
技术细节
编辑器技术栈
- Tiptap:基于 ProseMirror 构建的无头、框架无关的富文本编辑器
- ProseMirror:底层富文本编辑框架
- 粘贴处理:Tiptap 会原生解析剪贴板中的 HTML,并将其转换为内部文档模型
内容转换流程
Markdown file
↓ (Python markdown library)
HTML string
↓ (copy_to_clipboard.py)
System clipboard (text/html + text/plain)
↓ (Cmd+V keyboard shortcut)
Tiptap ProseMirror editor
↓ (auto-save)
Substack draft
支持的格式
以下 Markdown 元素在转换为 HTML 并粘贴后可正确呈现:
| Markdown 元素 | Substack 支持情况 | 备注 |
|---|---|---|
| 标题(H2-H6) | 是 | 不建议使用 H1(标题单独设置) |
| 粗体 / 斜体 | 是 | |
| 行内代码 | 是 | |
| 代码块 | 是 | 语法高亮可能有所不同 |
| 链接 | 是 | |
| 引用块 | 是 | |
| 无序列表 | 是 | |
| 有序列表 | 是 | |
| 水平分隔线 | 是 | |
| 表格 | 否 → 图片 | 使用 diagram-to-image 技能转换后,作为图片上传 |
| 图片 | 手动 | 通过 Image 工具栏按钮 → 文件选择器上传 |