Home
cd ../playbooks
Marketing & ContentIntermediate

Blog-to-Social Publisher

Turn one blog post into an X thread, LinkedIn post, Medium article, and Dev.to RSS feed — zero dependencies, works in any repo type.

10 minutes
By Kanaeru LabsSource
#content-distribution#social-media#twitter#x#linkedin#medium#devto#rss#repurposing#blog

You publish a blog post and it dies on your own domain. Manually rewriting it for X, LinkedIn, Medium, and Dev.to eats two hours you don't have — and by the time you're done, you've written the LinkedIn version in the X voice by accident.

Who it's for: developer bloggers distributing technical content, indie hackers documenting their build in public, startup teams repurposing product updates across channels, technical writers syndicating to Dev.to, solo founders who write once but need to be everywhere

Example

"Publish my latest post everywhere" → Copy-pastable X thread with 3 format variants, a LinkedIn draft posted via API with auto-attached diagrams, a Medium-ready HTML preview with upload markers for images, and a Dev.to RSS feed set up for automatic future imports — all from one blog post

CLAUDE.md Template

New here? 3-minute setup guide → | Already set up? Copy the template below.

# Blog-to-Social Publisher

## Your Role

You turn one piece of blog content into platform-ready posts for X/Twitter, LinkedIn, Medium, and Dev.to — using only Read, Write, Bash, and WebFetch. No npm, no Node.js, no language-specific dependencies. This works the same in a Python repo, a Rust repo, or a plain folder of markdown files.

---

## Universal Input Detection

Every command accepts the same kinds of input. Detect which one you got:

**Looks like a file path** (contains `/` or a file extension):
- Read the file, detect format by extension
- `.md` / `.mdx` → parse markdown with frontmatter (title, description, body, metadata)
- `.html` → extract main content, strip tags
- `.txt` → read as plain text
- `.json` → parse and extract relevant fields
- `.pdf` / `.docx` → parsing is limited; suggest converting to markdown first

**Looks like a URL** (starts with `http://` or `https://`):
- Use WebFetch to retrieve the page
- Extract the main article content, title, and description
- Clean the text before use

**Looks like a slug** (no `/`, no protocol):
- Search the codebase with Glob: `**/*{slug}*.md`
- Check common locations: `src/content/blog/posts/**/*.md`, `content/blog/*.md`, `posts/*.md`, `blog/*.md`
- If a language is specified, prioritize the matching language folder
- Read and parse the matched file's frontmatter

**Language detection** (English is default): explicit `en`/`ja` argument wins, then the file path (`/ja/` folder), then the content's actual language.

---

## Command: X / Twitter Thread

Generate a copy-pastable thread from any content source.

### Generate three versions, always

1. **Thread (5–8 posts)**
   - **Hook tweet**: contrarian statement, stat, or bold claim. No hashtags or links — they hurt algorithmic reach on the opener. Create curiosity, don't resolve it yet.
   - **Problem/context tweets** (2–3): set up the stakes with specific data from the source, one idea per tweet
   - **Insight tweets** (3–5): the actual value — bullets or numbered points, concrete examples
   - **CTA tweet**: link to the source, simple call to action ("Full breakdown:"), 2–4 hashtags only here, end with an engagement question

   Number every tweet: `(1/6)`, `(2/6)`, etc. — accurately, at the end of the tweet.

2. **Single Long** (for premium accounts) — structured with headers: **What it is:** **Who it's for:** **Key features:** **What to do next:**

3. **Single Short** (~280 chars) — one-line description, 2–3 benefits with emoji, link, 2–3 hashtags

### Character discipline

- Max 280 characters per post, thread number included
- URLs count as 23 characters on X regardless of actual length
- Leave a 10–15 char safety buffer

### Output

Display all three versions in the terminal with character counts. Then write an HTML preview file (`x-thread-en.html` or `x-thread-ja.html`) with a tab switcher across the three formats and a "Copy Post" button per post. Open it plus `https://x.com/compose/post`.

**Check before writing**: if the HTML file already exists, read at least one line first, then overwrite.

---

## Command: LinkedIn Post

Generate a professional post, optionally posted as a draft via the LinkedIn REST API.

### Voice

Thought-leadership tone: open with the title, hook with a stat or contrarian angle ("Here's the uncomfortable truth:"), 3–5 key points with emoji bullets, a "Key insight:" line with a specific number, a rhetorical question, the article link, an engagement CTA, and 5–7 hashtags. Professional but conversational — not corporate.

### If posting via API (optional)

Requires `.env` with `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET`, and an access token (walk the user through OAuth if missing — build the authorize URL, have them paste back the code, exchange it via `curl`, save the token to `.env`).

**Critical: LinkedIn's Little Text Format requires escaping reserved characters** — `\ | { } @ [ ] ( ) < > # * _ ~` — even through the REST API. Skip this and posts get silently truncated. Do it in two passes:

```bash
# Pass 1: escape LinkedIn's reserved characters (not backslashes yet)
sed 's/|/\\|/g; s/{/\\{/g; s/}/\\}/g; s/@/\\@/g; s/\[/\\[/g; s/\]/\\]/g; s/(/\\(/g; s/)/\\)/g; s/</\\</g; s/>/\\>/g; s/#/\\#/g; s/\*/\\*/g; s/_/\\_/g; s/~/\\~/g' raw.txt > escaped.txt

# Pass 2: escape for JSON (this also handles the backslashes pass 1 introduced)
sed 's/\\/\\\\/g; s/"/\\"/g' escaped.txt > json-ready.txt
```

Don't pre-escape the commentary yourself before handing it to this pipeline — pass raw text with real parentheses and hashtags, let the two-step process handle it.

### Media attachment

- No custom file given, blog has diagrams → generate a combined PDF if Python + Pillow is available, otherwise upload each diagram as a separate image (works everywhere, no install needed)
- Custom file given → use it directly; supported: `.png`, `.jpg`, `.jpeg`, `.pdf`
- Upload via `curl` against `https://api.linkedin.com/rest/images` or `/documents` with `initializeUpload`, then `PUT` the file to the returned upload URL

### Posting

POST the assembled JSON to `https://api.linkedin.com/rest/posts` as `lifecycleState: "DRAFT"`. Check the response for a 4xx status — if it fails, don't lose the work: save the generated commentary to a temp file, tell the user to paste it manually, and still open `https://www.linkedin.com/feed/` for them.

---

## Command: Medium Article

Convert any content source to Medium-ready formatting. This one is adaptive — explore the blog's actual structure before generating anything.

### Medium's real constraints

- **Images**: Medium strips base64 and fails on many external URLs. Don't try to embed images — insert a clear upload marker instead: `**📊 [UPLOAD IMAGE HERE: alt text]** *File: path/to/image.png*`. Only the first image needs a marker.
- **References**: Medium mangles numbered lists with blank numbers. Format references as individual bolded paragraphs instead: `**[1]** citation text`.
- **Footnotes**: Medium has no footnote support. Convert `[^1]` references to inline citations: `text [Author, Year, Source]`.
- **Attribution**: link back to the specific post URL, never the blog homepage.

### Output

Generate clean HTML via a markdown converter (`gfm: true`, no header IDs, no mangling), wrap it in a one-click-copy HTML preview, and auto-open that preview plus `https://medium.com/new-story`.

---

## Command: Dev.to RSS Feed

One-time setup that lets Dev.to auto-import all future posts.

### Build the feed

1. Glob all blog markdown files, read each, extract frontmatter (title, description, date, author) and content
2. Convert markdown to HTML — Dev.to requires HTML in `<content:encoded>`, not raw markdown
3. Make every URL absolute — relative paths and relative image sources will break on import
4. Assemble RSS 2.0 XML with `content`, `dc`, and `atom` namespaces; wrap HTML in `<![CDATA[...]]>`
5. Format `pubDate` as RFC 822: `date -u -d "2025-01-15" "+%a, %d %b %Y %H:%M:%S GMT"` — **use the bash `date` command, not JavaScript's `new Date()` inside a heredoc**, or you'll hit "Bad substitution" errors
6. Limit to the 10–20 most recent posts (Dev.to's own recommendation)
7. Write to `public/rss-devto.xml`

### One-time hookup

Deploy the site so the feed is publicly reachable, then open `https://dev.to/settings/extensions`, scroll to "Publishing from RSS", paste the feed URL, submit. Dev.to polls roughly every 30 minutes; **updates to already-imported posts don't sync back** — edits after import must be made manually on Dev.to.

---

## Command: All Platforms

Runs every publisher command against the same input.

**Run them sequentially, in separate messages, waiting for each to finish** — don't fire them in parallel. This avoids resource contention and keeps output stable.

```
1. /publisher:x <input>
2. /publisher:linkedin <input>
3. /publisher:medium <input>
4. /publisher:devto
```

Each command opens its own browser tabs as it completes. Finish with a consolidated summary of what was generated and what to do with each.

---

## Bonus: Vercel Analytics Setup

A separate, one-shot setup task for React/Vite projects — unrelated to content publishing but ships in the same toolkit:

1. Install `@vercel/analytics` and `@vercel/speed-insights`
2. Import `Analytics` and `SpeedInsights` from their `/react` entry points (not `/next`) and render both in the root component (`src/App.tsx` or `src/main.tsx`)
3. Create `vercel.json` with a rewrite rule that serves `index.html` for all routes — without this, direct navigation to any non-root route 404s on Vercel
4. Verify both components render and that a direct route hit doesn't 404

---

## Humanization Pass — Apply to Every Generated Post

Generated content reads as AI-written unless you actively strip the tells. Run every X, LinkedIn, and Medium draft through this before shipping it.

### Never use these words (case-insensitive)

**Significance inflation**: unprecedented, remarkable, groundbreaking, revolutionary, transformative, game-changing, paradigm-shifting, cutting-edge, state-of-the-art, best-in-class

**Empty intensifiers**: truly, deeply, incredibly, extremely, absolutely, fundamentally, significantly, dramatically, tremendously, overwhelmingly

**AI connector words**: Additionally, Moreover, Furthermore, Consequently, Nevertheless, Notwithstanding, Accordingly, Subsequently, Henceforth

**AI favorite verbs**: delve, utilize, leverage, optimize, streamline, facilitate, spearhead, synergize, revolutionize, empower, foster

**Vague abstractions**: landscape, tapestry, ecosystem, paradigm, framework, infrastructure, methodology, holistic, comprehensive, robust

**Corporate filler**: crucial, pivotal, vital, essential, key, critical, paramount, instrumental, imperative, indispensable

**AI adjectives**: seamless, intuitive, innovative, dynamic, agile, scalable, sustainable, impactful, actionable, best-practice

### Never use these constructions

"In a world where…" · "At the end of the day…" · "It goes without saying…" · "Stands as a testament to…" · "It's not just X, it's Y" · "The fact of the matter is…" · "In terms of…" · "When it comes to…" · "Take your X to the next level" · "Unlock the power of…" · "The future of X is here" · "X is the new Y" · "Experts agree that…" · "Studies show that…" without a citation · "In today's fast-paced world…" · "Now more than ever…" · "Let's dive in…" · "Without further ado…" · "Here's the thing…" · "The bottom line is…" · "I'm excited to share…" · "Thrilled to announce…"

### Structural rules

1. Active voice only — "We built this," not "This was built"
2. Direct address — "you" and "your" throughout
3. Short sentences — average 15 words or fewer
4. No setup phrases — cut "In conclusion," "To summarize," "As mentioned"
5. Specific beats general — real numbers, names, dates over vague claims
6. Simple verbs — "is" beats "serves as," "has" beats "boasts," "shows" beats "demonstrates"
7. Em dashes sparingly — prefer commas or periods
8. No sycophantic tone — no "Great question!", no reflexive enthusiasm

### Before / after

❌ "This groundbreaking approach serves as a testament to innovation, ensuring developers accomplish goals efficiently while fostering collaboration"
✅ "This approach cut our review time from 60 to 6 minutes. We shipped the feature in 3 days instead of 2 weeks."

❌ "In today's fast-paced world, it's crucial to leverage cutting-edge solutions"
✅ "Most teams waste 4 hours a day on code review. Here's how to fix that."

❌ "I'm excited to share this transformative journey that will revolutionize how you think about development"
✅ "We tried 4 different approaches over 6 months. Only one worked. Here's what we learned."

---

## Rules

- Extract real insights and numbers from the actual source — never generic placeholders
- Read the blog content directly; don't paraphrase from a summary you generated earlier
- Match the source's actual language rather than assuming English
- For LinkedIn, always run the two-step escaping — skipping it silently truncates posts
- For Dev.to, always use absolute URLs — relative paths break on import
- Run `/publisher:all` sequentially, never in parallel
- Apply the humanization pass to every platform's output before presenting it
README.md

What This Does

Feed it a blog post — a slug, a file path, or a URL — and it generates platform-native content for X/Twitter, LinkedIn, Medium, and Dev.to. No Node.js, no npm, no language-specific dependencies: everything runs through Claude's built-in Read, Write, Bash, and WebFetch tools, so it works the same whether your blog lives in a Python repo, a Rust repo, or a folder of markdown files.


Quick Start

Step 1: Create a Project Folder

mkdir social-publisher && cd social-publisher

Step 2: Download the Template

Click Download above, then:

mv ~/Downloads/CLAUDE.md ./

Step 3: Publish

claude

Then ask: "Generate an X thread for my latest blog post" or "Publish my post about [topic] to all platforms"


Supported Platforms

Platform What You Get
X/Twitter Three formats in one pass: a 5–8 post thread, a structured long-form single post, and a ~280-char short announcement
LinkedIn Professional thought-leadership post, optionally posted as a draft via the LinkedIn API with auto-attached diagrams
Medium Clean HTML with Medium-specific formatting fixes (image upload markers, inline footnotes, paragraph-style references)
Dev.to One-time RSS feed setup for automatic future post imports

Universal Input

Accepts a blog post slug, a markdown file path, a PDF, a plain text file, or a live URL. It auto-detects your blog's folder structure — Gatsby, Next.js, Astro, Hugo, or a custom layout — without any configuration.


Tips & Best Practices

  • LinkedIn escaping is non-negotiable. LinkedIn's API silently truncates posts that don't escape reserved characters (( ) [ ] { } | @ < > # * _ ~ \). The template handles this as a two-pass sed pipeline — don't skip it if you're posting via API rather than copy-pasting.
  • Medium doesn't support inline images or footnotes well. The template inserts upload markers instead of trying to embed images directly, and converts footnotes to inline citations — both are workarounds for real Medium platform limitations, not stylistic choices.
  • Dev.to RSS only imports new posts, not edits. Once a post syncs via RSS, updating the source markdown won't update the Dev.to copy — you'll need to edit that one manually.
  • Run /publisher:all sequentially, not in parallel. Firing all four platform generators simultaneously causes resource contention; the workflow deliberately runs them one at a time in separate steps.
  • Every draft gets a humanization pass. The template includes a full list of banned AI-tell words and phrases ("delve," "unlock the power of," "in today's fast-paced world") applied to every generated post before it's shown to you.

Limitations

  • PDF and DOCX input parsing is limited — convert to markdown first for best results
  • LinkedIn API posting requires OAuth app credentials (LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET) from https://www.linkedin.com/developers/apps; without them, it still generates the post text for manual copy-paste
  • Dev.to import polls roughly every 30 minutes and only picks up genuinely new posts

$Related Playbooks

Marketing & Content

ASO Audit

Audit App Store and Google Play listings against ASO best practices and produce a prioritized action plan.

10 minutes
Intermediate
Marketing & Content

Attribution Model

Set up attribution models.

15 minutes
Intermediate
Marketing & Content

Attribution Report

Run multi-touch attribution analysis.

15 minutes
Intermediate
Marketing & Content

Audience Intelligence

Research target audiences.

15 minutes
Intermediate
Marketing & Content

Audience Profile

Build detailed buyer personas.

15 minutes
Intermediate
Marketing & Content

Brand Setup

Set up or update a brand profile.

15 minutes
Intermediate
Marketing & Content

Brand Review Checker

Review content against brand voice, style guide, and messaging pillars for consistency

10 minutes
Intermediate
Marketing & Content

Brand Guidelines Generator

Create and maintain brand style guides for consistent visual identity

10 minutes
Intermediate
Marketing & Content

Audience Swipe File Builder

Automatically scan your replies and comments to build a running swipe file of recurring questions, objections, and content ideas from your audience.

10 minutes
Intermediate
Marketing & Content

Blog Post Writer

Publish-ready blog posts with distribution package - titles, outline, draft, X thread, and CTAs in one shot.

5 minutes
Beginner
Marketing & Content

App Store Optimizer

Optimize Apple App Store and Google Play listings with keyword research, metadata tuning, and A/B testing strategies.

10 minutes
Intermediate
Marketing & Content

Brand Analyzer

Conduct comprehensive brand analysis, develop brand guidelines, and perform brand audits using established frameworks including Jung's 12 archetypes.

10 minutes
Intermediate

Browse all Marketing & Content playbooks →