publish-substack-article

安装量: 50
排名: #14809

安装

npx skills add https://github.com/sugarforever/01coder-agent-skills --skill publish-substack-article
Publish Substack Article
Publish Markdown content to Substack post editor, converting Markdown to HTML and pasting as rich text. Saves as draft for user review before publishing.
Prerequisites
Browser automation MCP (either one):
Chrome DevTools MCP
(
mcp__chrome-devtools__*
)
Playwright MCP
(
mcp__playwright__*
)
User logged into Substack
Python 3 with
markdown
package (
pip install markdown
)
copy_to_clipboard.py
script (shared from publish-zsxq-article skill)
Browser MCP Tool Mapping
This skill works with both Chrome DevTools MCP and Playwright MCP. Use whichever is available:
Action
Chrome DevTools MCP
Playwright MCP
Navigate
navigate_page
browser_navigate
Take snapshot
take_snapshot
browser_snapshot
Take screenshot
take_screenshot
browser_take_screenshot
Click element
click
browser_click
Fill text
fill
browser_type
Press key
press_key
browser_press_key
Evaluate JS
evaluate_script
browser_evaluate
Detection
Check available tools at runtime. If
mcp__chrome-devtools__navigate_page
exists, use Chrome DevTools MCP. If
mcp__playwright__browser_navigate
exists, use Playwright MCP.
Key URLs
Substack dashboard:
https://{publication}.substack.com/publish
Post editor:
https://{publication}.substack.com/publish/post/{postId}
Default publication:
verysmallwoods
Editor Interface
The Substack post editor uses
Tiptap
(ProseMirror-based WYSIWYG editor).
Key Elements
Title input:
textbox "title"
(placeholder: "Title")
Subtitle input:
textbox "Add a subtitle…"
Content area:
.ProseMirror
(Tiptap editor, "Start writing...")
Save status:
button "Saved"
(auto-saves)
Preview button:
button "Preview"
Continue button:
button "Continue"
(publish flow - DO NOT USE)
Settings sidebar:
button "Settings"
(title, description, thumbnail)
Settings Sidebar (left panel)
When "Settings" or "File Settings" is open:
Title:
textbox "Add a title..."
Description:
textbox "Add a description..."
Thumbnail: Upload button (3:2 aspect ratio)
Toolbar
Bold, Italic, Strikethrough, Code, Link, Image, Audio, Video, Quote, Lists (bullet/ordered), Button, More (Code block, Divider, Footnote, LaTeX, etc.)
Content Insertion Method
CRITICAL: Use clipboard paste with HTML content
, NOT direct fill or plain Markdown paste.
The Tiptap editor handles HTML paste natively and renders it as rich content. The workflow is:
Convert Markdown to HTML using Python's
markdown
library
Copy HTML to system clipboard using
copy_to_clipboard.py html
Focus the editor content area
Press Cmd+V (macOS) or Ctrl+V (Windows/Linux) to paste
Why HTML paste?
fill
tool → Content treated as plain text, no formatting
Plain Markdown paste → Tiptap does NOT parse Markdown on paste
HTML paste → Tiptap renders HTML as rich content (headings, code blocks, links, bold, etc.)
Known limitation
Substack's editor does NOT support HTML tables. Tables will be collapsed into plain text. See Step 0: Pre-Processing for converting tables to images. Main Workflow Step 0: Pre-Processing — Convert Tables to Images Substack does NOT render HTML tables. They collapse into plain text. Any Markdown table must be converted to a PNG image and uploaded separately. Workflow: Detect tables in the Markdown file (lines with | forming table structure) Convert each table to PNG using the diagram-to-image skill:

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
Note the position
of each table in the article for later insertion (after which heading/paragraph)
Remove table Markdown
from the content before HTML conversion (so it won't appear as plain text in the pasted content)
Image upload
happens after pasting the main content — see Step 7.
Step 1: Prepare Content
Read the Markdown file and extract:
Title
from YAML frontmatter title field, or H1 header

Title

, or filename
Subtitle
from YAML frontmatter
excerpt
or
description
field
Content
full Markdown body (strip YAML frontmatter and any cross-reference links) Step 2: Convert Markdown to HTML Use Python's markdown library with tables and fenced_code extensions: 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
)
IMPORTANT
Do NOT use nl2br extension - it converts single newlines to
tags, causing extra line breaks in the editor. Step 3: Navigate to Substack Navigate to the Substack dashboard and create a new post:

Navigate to Substack dashboard

navigate to: https://verysmallwoods.substack.com/publish
If not logged in, prompt user to log in:
请先登录 Substack,登录完成后告诉我。
Please log in to Substack first, then let me know.
Step 4: Create New Post
From the dashboard, create a new text post:
Click "Create new" in the sidebar
Select "Text post" (or navigate directly to a new post URL)
Alternatively, if the editor is already open with an empty post, proceed directly.
Step 5: Fill Title and Subtitle
Click the title textbox (
textbox "title"
)
Type the article title
Click the subtitle textbox (
textbox "Add a subtitle…"
)
Type the subtitle/excerpt
click: title textbox
fill/type: article title
click: subtitle textbox
fill/type: article subtitle
Step 6: Insert HTML Content (via Clipboard Paste)
CRITICAL: Do NOT use
fill
tool
- it inserts plain text without formatting.
Copy HTML to system clipboard:
python3 /path/to/copy_to_clipboard.py html
--file
/tmp/substack_article.html
Click the editor content area (
.ProseMirror
or paragraph element inside it)
Press Cmd+V to paste:
press_key: Meta+v (macOS)
press_key: Control+v (Windows/Linux)
This triggers Tiptap's HTML paste handler, which renders the content as rich text with proper formatting.
Step 7: Insert Table Images
If the article had tables converted to images in Step 0, insert them now:
Navigate to the correct position
in the editor — click on the paragraph or empty line where the table should appear (after the relevant heading/text)
Click the Image toolbar button
(
button "Image"
) — a dropdown menu appears with options: Image, Gallery, Stock photos, Generate image
Click "Image" menuitem
from the dropdown — a file chooser dialog opens
Upload the image
via file chooser:
Playwright MCP:
browser_file_upload
with the image path
Chrome DevTools MCP:
upload_file
with the image path
Important notes:
File path restriction
Playwright MCP only allows file uploads from within allowed roots (project directories). If your image is in
/tmp/
, copy it to the project directory first
Repeat for each table
Position cursor at the correct location, then upload each table image
Delete residual text
If table content was pasted as plain text (because it wasn't removed in pre-processing), select it (triple-click to select paragraph) and delete before inserting the image
Step 8: Verify Draft
After pasting:
Check the "Saved" status indicator (green dot + "Saved" text)
Take a snapshot to verify content structure
Optionally take a screenshot for visual verification
The editor auto-saves, so no explicit save action is needed.
Step 9: Report Completion
草稿已保存到 Substack。请在 Substack 中预览并手动发布。
Draft saved to Substack. Please preview and publish manually.
Post URL: https://verysmallwoods.substack.com/publish/post/{postId}
Complete Example Flow
User: "把 /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
- "草稿已保存,请手动预览并发布"
Critical Rules
NEVER click "Continue"
- This starts the publish flow. Only save as draft (auto-save handles this)
Always convert to HTML first
- Plain Markdown will not be parsed by the Tiptap editor
Use clipboard paste
- The only reliable way to insert formatted content
Check login status
- Prompt user to login if needed
Preserve original file
- Never modify the source Markdown file
Report completion
- Tell user the draft is saved and needs manual review
No
nl2br
extension
- Causes double line breaks
Tables → images
- Pre-process tables before pasting content; upload images after paste
Playwright file paths
- Playwright MCP restricts file uploads to allowed roots; copy temp files to project directory before uploading
Troubleshooting
Content Shows as Plain Text (No Formatting)
If you see raw HTML tags or unformatted text:
Cause
Content was inserted using
fill
tool instead of clipboard paste
Solution
Use the
copy_to_clipboard.py
+ Cmd+V method (see Step 6)
Tables Not Rendering (Shows Plain Text)
Substack's Tiptap editor does not support HTML tables. They collapse into inline plain text.
Solution
Convert tables to PNG via diagram-to-image skill → upload as images (see Step 0 and Step 7)
Alternative
Restructure simple tables as formatted lists
If plain text already pasted
Triple-click the plain text paragraph to select it, press Backspace to delete, then insert the table image at that position
Login Required
If page shows login prompt:
请先登录 Substack: https://verysmallwoods.substack.com
登录完成后告诉我。
Editor Not Loading
If editor elements are not visible:
Wait for page to fully load
Take a new snapshot
If still not loading, refresh the page
Clipboard Copy Fails
If
copy_to_clipboard.py
fails:
Ensure dependencies:
pip install pyobjc-framework-Cocoa
(macOS)
Check the HTML file exists and is readable
Try copying a smaller test string first
Element Reference
Element
Selector/Identifier
Description
Title input
textbox "title"
Post title
Subtitle input
textbox "Add a subtitle…"
Post subtitle
Content area
.ProseMirror
(Tiptap editor)
Post content
Save status
button "Saved"
Auto-save indicator
Preview button
button "Preview"
Preview post
Continue button
button "Continue"
DO NOT USE - starts publish flow
Settings button
button "Settings"
Open settings sidebar
Exit button
button "Exit"
Exit editor
Image button
button "Image"
Opens image upload dropdown
Image menuitem
menuitem "Image"
Opens file chooser for image upload
Author button
button "{PublicationName}"
Author/publication selector
Technical Details
Editor Stack
Tiptap
A headless, framework-agnostic rich-text editor built on ProseMirror
ProseMirror
The underlying rich-text editing framework
Paste handling
Tiptap natively parses HTML from clipboard and converts to its internal document model Content Conversion Pipeline 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 Supported Formatting The following Markdown elements are correctly rendered after HTML conversion and paste: Markdown Element Substack Support Notes Headings (H2-H6) Yes H1 not recommended (title is separate) Bold / Italic Yes Inline code Yes Code blocks Yes Syntax highlighting may vary Links Yes Blockquotes Yes Bullet lists Yes Ordered lists Yes Horizontal rules Yes Tables No → Image Convert via diagram-to-image skill, upload as image Images Manual Upload via Image toolbar button → file chooser
返回排行榜