name: crossposting description: Crosspost Wasp blog articles (MDX) to DEV.to and Medium.
MDX Conversion Script
Converts a Wasp blog MDX file to clean markdown, HTML, and Medium-ready chunks. Output is saved to .claude/skills/crossposting/scripts/output/.
npx tsx .claude/skills/crossposting/scripts/convert-mdx.ts <path-to-mdx-file> [--publish-devto] [--update-devto <id>] [--upload-videos]
Output files (always written):
<slug>.md— clean markdown (for DEV.to)<slug>.html— HTML (for Medium preview/reference)<slug>-medium-chunks.json—{ title: string, chunks: string[] }pre-split at<h2>boundaries, pre-escaped for JS template literals
Flags:
--publish-devto— POST as draft to DEV.to (requiresDEVTO_API_KEY)--update-devto <id>— PUT to existing DEV.to article (requiresDEVTO_API_KEY)--upload-videos— upload local .mp4s to YouTube (unlisted), embed as{% youtube URL %}liquid tags
Env vars: DEVTO_API_KEY (from https://dev.to/settings/extensions), YOUTUBE_CLIENT_ID + YOUTUBE_CLIENT_SECRET (saved in 1password for the users).
Notes:
canonical_urlis auto-generated from MDX filename →wasp.sh/blog/...- Without
--upload-videos, local .mp4 videos become plain links
YouTube Setup (one-time, human steps)
- Create Google Cloud project → enable YouTube Data API v3 → create OAuth 2.0 Desktop credentials
- Add
YOUTUBE_CLIENT_IDandYOUTUBE_CLIENT_SECRETto~/.zshrc - Run
npx tsx .claude/skills/crossposting/scripts/upload-youtube.ts --auth→ authorize in browser (select @wasplang channel if prompted) - Refresh token stored in
~/.youtube-upload-tokens.json - Check channel:
npx tsx .claude/skills/crossposting/scripts/upload-youtube.ts --whoami. Wrong channel? Delete token file and re-auth.
Crossposting to Dev.to
Use --publish-devto or --update-devto <id> flags. Requires DEVTO_API_KEY.
Crossposting to Medium
Paste HTML into Medium's editor via Chrome DevTools MCP. The user must be logged in to Medium using email login (not google oauth) with info@wasp-lang.dev.
Steps
1. Convert the MDX article
Run the conversion script, if it hasn't been run yet (see usage above).
Images: Medium does not support webp. Convert webp images to jpg for later manual upload:
for f in static/img/<slug>/*.webp; do sips -s format jpeg "$f" --out ".claude/skills/crossposting/scripts/output/$(basename "${f%.webp}.jpg")"; done
2. Navigate to https://medium.com/new-story
The human user must login to Medium with the info@wasp-lang.dev email and get the OTP sent to your email.
3. Set the article title
Do NOT use the fill tool — it puts text into the body paragraph instead. Use evaluate_script with the setMediumTitle function from scripts/medium-helpers.js.
- Read
setMediumTitlefromscripts/medium-helpers.js. - Replace
TITLE_PLACEHOLDERwith the actual title (escape backticks and${sequences). - Call
evaluate_scriptwith the resulting function string.
Then press Enter to move cursor to the body area.
4. Paste the article body
IMPORTANT: Do NOT use document.execCommand — it bypasses Medium's internal state and causes save errors. Always use synthetic ClipboardEvent paste.
IMPORTANT: Chunks are already pre-escaped for JS template literals and pre-processed for Medium (YouTube URLs, image placeholders). Inline each chunk string directly into the pasteMediumChunk function body where HTML_CHUNK_PLACEHOLDER appears. Do NOT base64-encode, store chunks in page variables, or add any intermediate encoding steps.
- Read
output/<slug>-medium-chunks.json— chunks are ready to paste as-is. - Click the body paragraph to focus it.
- For each chunk, use
evaluate_scriptwith thepasteMediumChunkfunction fromscripts/medium-helpers.js. ReplaceHTML_CHUNK_PLACEHOLDERdirectly with the chunk string content. - Wait for "Saved" status after all chunks are pasted.
5. Verify
- Snapshot the article to confirm it looks correct and shows "Draft" / "Saved"
- Scroll through to confirm section order matches the original and make any necessary adjustments.
6. Before Publishing (human steps)
- Replace
[IMAGE: filename.jpg]markers with actual image uploads in Medium's editor - Add any YouTube embeds that didn't auto-embed (Medium auto-embeds standalone YouTube URLs in
<p>tags) - Add the banner image and canonical URL
- Review and publish from Medium's UI