smithery/DidierRLopes

convert-newsletter

Converts Substack (or Beehiiv) newsletter posts into properly formatted blog markdown files. Use when converting newsletters to blog posts, importing Substack/Beehiiv content, or creating blog posts from newsletter URLs.

Installation

$ npx skills add smithery/DidierRLopes --skill convert-beehiiv-newsletter

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Allowed toolsRead, Write, Bash, Glob, Grep, mcp__fetch__imageFetch, WebFetch

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 12,752 B
  • docs SUMMARY.md 231 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Converting Newsletter to Blog Post

A comprehensive guide for converting Substack (or Beehiiv) newsletter posts into properly formatted blog markdown files for the website.

Supported Platforms

  • Substack (primary): https://didierrlopes.substack.com/p/<slug> or https://substack.com/home/post/p-<id>
  • Beehiiv (legacy): https://didierlopes.beehiiv.com/p/<slug>

Prerequisites

  • Access to the newsletter URL
  • Image extraction capabilities (use mcpfetchimageFetch tool)
  • Understanding of the blog's markdown structure and front matter requirements

Step-by-Step Conversion Process

1. Extract Newsletter Content

Use the imageFetch tool to extract content and images:

mcp__fetch__imageFetch with url=<newsletter-url> and images={"output": "file", "layout": "individual", "maxCount": 10}

Platform-specific notes:

Substack:

  • Content is usually well-extracted via imageFetch in markdown mode
  • For posts accessed via substack.com/home/post/p-<id>, the content may be embedded in JSON within the HTML. Use raw=true and extract the body_html field from the embedded JSON
  • Clean Substack tracking params: remove ?utmsource=didierlopes.beehiiv.com&utmmedium=newsletter&utm_campaign=... from URLs
  • Substack image URLs follow the pattern: https://substackcdn.com/image/fetch/.../https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F<uuid>_<dimensions>.<ext>
  • To download high-res images, construct URL: https://substackcdn.com/image/fetch/w1200,climit,fpng,qauto:good/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F<uuid>_<dimensions>.<ext>

Beehiiv:

  • Content is directly extractable via imageFetch
  • Beehiiv CDN image URLs may expire, so download immediately

Key extraction requirements:

  • Capture all text content including headings, paragraphs, and lists
  • Extract all images in high quality (minimum 1000px width)
  • Preserve link URLs and their context
  • Note any special formatting (bold, italics, quotes)
  • CRITICAL: The blog post content must be 1:1 with the original newsletter (excluding ads). Do not summarize, paraphrase, or restructure. Copy the exact content, structure, and formatting.
  • IMPORTANT: Use WebFetch with a prompt to extract ALL important links:

- GitHub repository URLs (especially for open source project posts) - YouTube video URLs (these need to be embedded as iframes) - Links to other newsletter posts (convert to internal blog links if already converted) - Any other substantive links mentioned in the content

2. Get Publication Date

Format: YYYY-MM-DD

Substack:

  • Fetch the archive page: https://didierrlopes.substack.com/archive
  • Use raw mode and extract post titles alongside dates with pattern matching
  • Dates appear as short format (e.g., "Feb 24", "Mar 6") next to post titles
  • The slug may contain a date suffix (e.g., the-era-of-on-demand-software-26-01-31) but do NOT use it - it may not match the actual publication date
  • Always verify against the archive page

Beehiiv:

Rules:

  • Never use today's date - always use the actual publication date
  • Create slug from title: lowercase, replace spaces with hyphens, remove special characters
  • Example: "The trampoline job: Optimize your career for growth" → 2025-09-19-the-trampoline-job-optimize-your-career-for-growth.md

3. Create Front Matter

---
slug: <title-slug-without-date>
title: <Full Newsletter Title>
date: <YYYY-MM-DD>
image: /blog/YYYY-MM-DD-slug/hero.webp
tags:
- <relevant-tag-1>
- <relevant-tag-2>
- <relevant-tag-3>
description: <Newsletter subtitle or first paragraph summary (max 160 chars)>
hideSidebar: true
---

Front Matter Rules:

  • slug: Use title in kebab-case without the date prefix
  • title: Exact newsletter title, properly capitalized
  • date: Newsletter publication date in YYYY-MM-DD format (from archive page, NOT today's date)
  • image: Points to the hero image path inside the post asset folder (WITH .webp extension). If the post has no cover image, omit this field
  • tags: Extract 3-6 relevant tags from content themes (lowercase)
  • description: Use newsletter subtitle or create compelling summary
  • hideSidebar: Set to true for blog posts

4. Process Images

IMPORTANT: All images must be in WebP format for optimal file size and fast page loads.

Image Handling Rules:

  1. Hero Image

- Substack: Fetch from the archive page (https://didierrlopes.substack.com/archive). Extract thumbnail image URLs which follow the pattern with public%2Fimages%2F<uuid>. Download at high resolution using: https://substackcdn.com/image/fetch/w1200,climit,fpng,qauto:good/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F<uuid><dimensions>.<ext> - Beehiiv: Fetch the hero image from https://didierlopes.beehiiv.com/ main page - Some posts may use YouTube thumbnails as cover images (URL pattern: https://substackcdn.com/image/youtube/w728,climit/<videoid>) - Download to /static/blog/YYYY-MM-DD-slug/hero.png (or .jpg) first, then convert to WebP - Note: The image must be saved in the post's static/blog/YYYY-MM-DD-slug/ directory, not just /blog/ - If a post has no cover image at all, omit the image: field from front matter

  1. Content Images

- Download to /static/blog/YYYY-MM-DD-slug/N.png (where N is sequential number) - Download from Substack/Beehiiv CDN URLs - Maintain aspect ratio - Note: Save in the post's static/blog/YYYY-MM-DD-slug/ directory

  1. Convert All Images to WebP

After downloading images, convert them to WebP format using: ```bash # For PNG/JPG images: cwebp -q 90 "image.png" -o "image.webp" && rm "image.png"

# For GIF images (animated): gif2webp -q 90 "image.gif" -o "image.webp" && rm "image.gif" ```

Final filenames should be: - Hero: /static/blog/YYYY-MM-DD-slug/hero.webp - Content: /static/blog/YYYY-MM-DD-slug/N.webp

  1. Image Markdown Format

```markdown ![Alt text description](/blog/YYYY-MM-DD-slug/image-path.webp)

<!-- For centered images with custom width --> <p align="center"> <img width="500" src="/blog/YYYY-MM-DD-slug/image-path.webp" alt="Description" /> </p> ```

5. Convert Content Structure

Content Conversion Rules:

  1. Opening Section

- Do NOT add hero image in the content (it's handled automatically via the front matter image field) - Include newsletter subtitle as opening paragraph - Add <!-- truncate --> after intro paragraph for blog preview

  1. Section Dividers

- Generally not needed between sections - Let content flow naturally without visual breaks - Only use if there's a major topic shift that requires clear separation

  1. Headings

- Newsletter H3 → Blog H2 (##) - Newsletter H4 → Blog H3 (###) - Maintain heading hierarchy

  1. Lists

- Preserve bullet points as markdown lists (-) - Maintain indentation for nested lists - Important: Add <br /> after each list block for proper spacing

Example: ```markdown - First item - Second item - Third item

<br />

Next paragraph starts here... ```

  1. Links

- Substack: Remove tracking parameters (?utmsource=...&utmmedium=...&utm_campaign=...) - Beehiiv: Convert Beehiiv tracking URLs to original URLs - Format: [link text](url) - For tweets/social embeds, reference as: This [post](url) - Convert links to other newsletter posts to internal blog links if already converted (e.g., https://didierlopes.com/blog/<slug>)

  1. Emphasis

- Bold text: text - Italic text: text

  1. Quotes and Citations

- Use blockquote syntax (>) for extended quotes - For multi-paragraph quotes, use > <br /> between paragraphs - Add <br /> after the quote block - IMPORTANT: Extract the actual hyperlink from the newsletter, not guess or create new ones - Include attribution with author name and link when available

Example: ```markdown > First paragraph of the quote goes here. > > <br /> > > Second paragraph of the quote continues here.

<br />

Author Name - "Article Title" ```

  1. Code/Technical Content

- Wrap technical terms in backticks: \term\ - Use code blocks for snippets

  1. YouTube Videos

- IMPORTANT: Extract the actual YouTube URL from the newsletter content - Convert YouTube links to embedded iframe format - Extract video ID from URL (e.g., https://www.youtube.com/watch?v=VIDEOID → VIDEOID) - Use only the video ID in the embed URL, no additional parameters - Use responsive embed with centered layout - IMPORTANT: Add <br /> after the video embed so following text doesn't appear glued to it

Example conversion: - Original: https://www.youtube.com/watch?v=Zyw-YA0k3xo - Embed format: ```html <div className="flex place-items-center justify-center items-center rounded-sm mx-auto"> <iframe src="https://www.youtube.com/embed/Zyw-YA0k3xo"; width="800" height="400" /> </div>

<br /> ```

Note: Always verify the video link exists in the original content - don't assume or guess video IDs

  1. Image Captions

- If text immediately following an image is a caption/description of that image, style it differently - Use smaller font size and bring it closer to the image with negative margin

Example: ``html <p align="center"> <img width="800" src="/blog/YYYY-MM-DD-slug/image.webp" alt="Description" /> </p> <p align="center" style={{fontSize: '0.85em', marginTop: '-0.5em'}}>Caption text describing the image above.</p> ``

6. Content Cleanup

Remove from Newsletter:

  • Footer/unsubscribe links
  • Newsletter-specific CTAs (subscribe buttons, share widgets)
  • Tracking parameters from URLs (?utm_source=...)
  • Newsletter metadata (view in browser links)
  • Substack "No posts" artifacts at the bottom
  • "A quick note: I've moved this newsletter from Beehiiv to Substack" migration notices (unless contextually important)

Preserve:

  • Author voice and tone
  • All substantive content
  • External references and citations
  • Story flow and narrative structure

7. Quality Checks

Before finalizing:

  • Front matter is complete and valid YAML
  • Front matter image: field includes .webp extension (or is omitted if no cover image)
  • All images are downloaded, converted to WebP, and properly referenced
  • All image references in content use .webp extension
  • Links are clean (no tracking parameters)
  • Markdown syntax is valid
  • Content flows naturally without newsletter artifacts
  • File is saved in /blog/ directory with correct naming
  • Test render locally to ensure formatting

Example Conversions

Substack URL: https://didierrlopes.substack.com/p/the-context-wars-in-financial-services Converted to: /blog/2026-02-13-the-context-wars-in-financial-services.md

Beehiiv URL: https://didierlopes.beehiiv.com/p/the-trampoline-job-optimize-your-career-for-growth Converted to: /blog/2025-09-19-the-trampoline-job-optimize-your-career-for-growth.md

Key transformations:

  • Extracted images and converted to WebP
  • Converted newsletter sections to H2/H3 headings
  • Cleaned URLs of tracking parameters
  • Added proper front matter with relevant tags
  • Preserved personal narrative and bullet points

Common Pitfalls to Avoid

  • Don't include newsletter-specific language ("Click here to read more")
  • Don't forget to download images (CDN links may expire)
  • Don't use relative dates ("last week") - use specific dates
  • Don't include email-specific formatting (table layouts for email clients)
  • Don't forget the <!-- truncate --> marker for blog preview
  • Don't trust date suffixes in Substack slugs - always verify against the archive page
  • Don't use substack.com/home/post/p-<id> URLs directly for content extraction - try didierrlopes.substack.com/p/<slug> first as it renders better