SY

syndicate-article

Syndicates blog posts to developer and crypto communities with optimized hooks.

Install

mkdir -p .claude/skills/syndicate-article-dawsonblock && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16097" && unzip -o skill.zip -d .claude/skills/syndicate-article-dawsonblock && rm skill.zip

Installs to .claude/skills/syndicate-article-dawsonblock

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Cross-post articles to Dev.to and Farcaster with hook-driven copy and click-optimized metadata
94 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Cross-post articles to Dev.to
  • Cross-post articles to Farcaster
  • Extract hook-driven copy from articles
  • Generate click-optimized metadata for Dev.to
  • Build canonical URLs for SEO attribution

How it works

It extracts content from an article, cleans it for syndication, generates channel-specific hooks and metadata, and posts to Dev.to and Farcaster with canonical URLs.

Inputs & outputs

You give it
article filename (e.g., repo-article-2026-04-16.md)
You get back
syndicated posts on Dev.to and Farcaster, log of syndication activity

When to use syndicate-article

  • Cross-posting blog articles
  • Syndicating tech content
  • Improving article reach

About this skill

<!-- autoresearch: variation B — sharper output: hook-driven cast + CTR-optimized Dev.to card, with quality gate -->

${var} — Filename of a specific article to syndicate (e.g. repo-article-2026-04-16.md). If empty, syndicates the most recently written article.

Cross-post Aeon articles to Dev.to (developer audience) and Farcaster (crypto-native audience) for organic discovery. Articles are published with a canonical URL pointing back to the GitHub Pages gallery, preserving SEO attribution.

Each channel is opt-in — set the relevant secrets and it activates. If neither is configured, the skill logs a skip and exits silently.

Thesis: The biggest lever in syndication is not "did we post" but "will anyone click." Generic "New post: X\n\nURL" casts and body-only Dev.to articles waste the channel's attention budget. This skill extracts a real hook from the article, adds a cover image and description for Dev.to, and refuses to post if no hook exists.

Prerequisites

  • DEVTO_API_KEY — Dev.to API key. Generate at https://dev.to/settings/extensions (scroll to "DEV Community API Keys").
  • NEYNAR_API_KEY + NEYNAR_SIGNER_UUID — Neynar credentials for Farcaster posting. Get an API key at neynar.com and create a managed signer to obtain the signer UUID.

If none of DEVTO_API_KEY or NEYNAR_SIGNER_UUID are set, the skill logs a skip and exits silently — no error, no notification.

Steps

1. Channel check

if [ -z "$DEVTO_API_KEY" ] && [ -z "$NEYNAR_SIGNER_UUID" ]; then
  echo "SYNDICATE_SKIP: no syndication channels configured"
  exit 0
fi

Log SYNDICATE_SKIP: no syndication channels configured to memory/logs/${today}.md and stop. Do NOT send any notification.

2. Select the article

  • If ${var} is set, use articles/${var}.
  • Otherwise, most recently modified .md in articles/ (exclude feed.xml, .gitkeep):
    ls -t articles/*.md 2>/dev/null | grep -v -E '(feed\.xml|\.gitkeep)$' | head -1
    
  • If no articles exist, log SYNDICATE_SKIP: no articles found and stop.

3. Dedup check

Search the last 7 days of memory/logs/ for:

  • SYNDICATED: lines containing this filename → Dev.to already posted
  • FARCAST: lines containing this filename → Farcaster already queued/posted

Track per-channel. If both already posted, log SYNDICATE_SKIP: already syndicated {filename} to all channels and stop. Otherwise proceed with only the missing channels.

4. Parse the article

  • Title: first # Heading. If Jekyll frontmatter title: exists, use that.
  • Body (raw): everything after the first heading (or after frontmatter).
  • Date: regex ([0-9]{4}-[0-9]{2}-[0-9]{2}) on filename.
  • Slug: filename prefix before the date, trailing hyphens stripped.
  • Cover image (cover_url): if Jekyll frontmatter has image: or cover:, use that; otherwise first ![alt](url) in the body where url starts with http. If none found, leave empty.
  • Description (meta_description): first paragraph of the body after the title — stripped of markdown, trimmed to 140 chars, ending on a word boundary. Used for Dev.to description and Farcaster hook fallback.

5. Clean the body for syndication

Produce body_clean from the raw body:

  1. Remove any Jekyll liquid tags ({% ... %}, {{ ... }}) — they render as literal text on Dev.to.
  2. Rewrite relative links/images: any ](/foo) or ](foo.md) → absolute https://aaronjmars.github.io/aeon/foo (strip .md where present). Preserve anchor fragments.
  3. Strip the first # Heading line (Dev.to shows the title separately — double-heading looks amateur).
  4. Trim leading/trailing whitespace.

Keep the pre-cleaned body around as a source for step 6's hook extraction.

6. Extract the Farcaster hook (quality gate)

Farcaster's feed rewards specificity. "New post: Title\nURL" produces near-zero engagement. Extract a real hook from the article:

Hook candidates (try in order, stop at first that passes):

  1. Explicit TL;DR — if the article has a ## TL;DR, ## Summary, or **TL;DR:** block, use its first sentence.
  2. First claim paragraph — the first paragraph of the body that is NOT:
    • A question title (ends with ? and <60 chars)
    • Boilerplate ("In this article...", "Today we'll...", "This post covers...")
    • A frontmatter echo (repeats the title)
    • A code block, table, list, or image
    • Shorter than 40 chars or longer than 400 chars
  3. Strongest line — scan the first 800 chars of the body for the most specific sentence: contains a number, a proper noun, OR a concrete claim verb ("shipped", "found", "broke", "dropped", "crossed", "beat"). Use that line.

Trim the chosen hook to 240 chars, ending on a word boundary. This leaves ~60 chars for the URL within Farcaster's 320-char limit.

Quality gate: If none of the three strategies produce a hook ≥40 chars, set hook_found=false. Skip the Farcaster step entirely and log FARCAST_SKIP: no hook extractable from {filename}. Do not fall back to "New post: X" — a weak cast is worse than no cast (burns attention, trains followers to scroll past).

7. Build the canonical URL

https://aaronjmars.github.io/aeon/articles/YYYY/MM/DD/<slug>/

Where <slug> matches update-gallery's Jekyll post filename convention: title lowercased, spaces → hyphens, non-alphanumerics stripped, truncated to 50 chars.

8. Dev.to post (if enabled + not already syndicated)

a. Derive tags (max 4, Dev.to hard limit) from the filename slug:

  • repo-article, articleai, github, automation, agents
  • token-report, token-alert, defi-overview, defi-monitorcrypto, defi, blockchain, trading
  • changelog, push-recap, shiplogopensource, devops, changelog, github
  • digest, rss-digest, hacker-newsnews, tech, ai, digest
  • deep-research, research-brief, paper-pickresearch, ai, machinelearning, papers
  • technical-explainertutorial, ai, explainer, programming
  • Everything else → ai, automation, agents, programming

b. Write the payload to .pending-devto/<slug>-<date>.json (always use the post-process path; WebFetch cannot reliably pass api-key headers from the sandbox):

mkdir -p .pending-devto/

Payload:

{
  "article": {
    "title": "<extracted title>",
    "body_markdown": "<body_clean>",
    "published": true,
    "tags": ["tag1", "tag2", "tag3", "tag4"],
    "canonical_url": "<canonical_url>",
    "description": "<meta_description>",
    "main_image": "<cover_url or empty>",
    "series": "Aeon"
  }
}

Omit main_image from the JSON entirely if cover_url is empty (Dev.to rejects empty-string URLs). Omit description if <20 chars (better to let Dev.to auto-excerpt than feed it garbage).

c. scripts/postprocess-devto.sh POSTs to https://dev.to/api/articles and records the URL on success.

d. Record in memory/logs/${today}.md:

SYNDICATED: {filename} → {canonical_url} (queued for Dev.to, see postprocess log for dev.to URL)

(The Dev.to URL is only known after the postprocess run — the log line matches filename for dedup; a future reconciliation skill or manual check picks up the live URL.)

9. Farcaster cast (if enabled + hook_found + not already syndicated)

a. Build the cast text (320-byte Farcaster limit):

<hook>

<canonical_url>

No "New post:" prefix, no emoji, no hashtags — the hook IS the value. Verify total byte length ≤ 310 (leave 10 bytes buffer for embed unfurl metadata). If over, trim the hook further on a word boundary.

b. Write the payload to .pending-farcaster/<slug>-<date>.json — do NOT include NEYNAR_SIGNER_UUID:

{
  "text": "<cast text>",
  "embeds": [{"url": "<canonical_url>"}]
}

Use mkdir -p .pending-farcaster/ first.

c. scripts/postprocess-farcaster.sh reads each payload, injects NEYNAR_SIGNER_UUID from env, POSTs to https://api.neynar.com/v2/farcaster/cast with x-api-key: $NEYNAR_API_KEY, removes on success.

d. Record in memory/logs/${today}.md:

FARCAST: {filename} → queued (hook: "{first 60 chars of hook}...")

10. Notification

Send via ./notify only if at least one channel was actually queued (not skipped). Match operator voice — direct, concrete, no hype.

If both Dev.to + Farcaster queued:

Syndicated "{title}"

Dev.to: queued with cover image and description.
Farcaster: hook ready — "{first 80 chars of hook}..."

Canonical: {canonical_url}

If only Dev.to (Farcaster skipped on quality gate or missing secret):

Syndicated "{title}" to Dev.to

Farcaster skipped ({reason: no hook extractable / not configured}).

Canonical: {canonical_url}

If only Farcaster (Dev.to skipped or missing secret):

Cast queued for "{title}"

Hook: "{first 80 chars}..."

Canonical: {canonical_url}

If nothing queued (both already syndicated, or neither passed gates), do NOT notify.

Sandbox note

  • Dev.to: Always writes to .pending-devto/. scripts/postprocess-devto.sh executes the actual API call after Claude finishes, outside the sandbox. Avoids the env-var-in-headers problem entirely.
  • Farcaster: Writes .pending-farcaster/<slug>-<date>.json (no signer_uuid on disk); scripts/postprocess-farcaster.sh injects the signer UUID from env at post time and POSTs to Neynar.

Why the quality gate matters

Dropping weak casts is a feature, not a bug. Each low-effort cast trains followers to scroll past the next one — the compounding cost of "new post: X" over 100 posts is worse than posting 60 with hooks and skipping 40. If the article doesn't yield an extractable hook, that's signal the article needs a stronger opener; fix the article, don't launder the cast.

Output (summary block)

End with:

## Summary
- Article: {filename}
- Dev.to

---

*Content truncated.*

When not to use it

  • When no syndication channels (Dev.to or Farcaster) are configured
  • When no hook can be extracted from the article for Farcaster
  • When the article has already been syndicated to all configured channels

Prerequisites

DEVTO_API_KEY?NEYNAR_API_KEY?NEYNAR_SIGNER_UUID?

Limitations

  • Requires DEVTO_API_KEY for Dev.to posting
  • Requires NEYNAR_API_KEY and NEYNAR_SIGNER_UUID for Farcaster posting
  • If no hook can be extracted, Farcaster posting is skipped

How it compares

This skill automates article syndication with a focus on engagement and SEO, using specific strategies to create compelling posts for each platform, unlike simple content duplication.

Compared to similar skills

syndicate-article side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
syndicate-article (this skill)02moReviewIntermediate
citedy-content-writer03moNo flagsIntermediate
seo-optimizer338moNo flagsIntermediate
seo-content-writer244moNo flagsBeginner

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

citedy-content-writer

citedy

>

00

seo-optimizer

davila7

Search Engine Optimization specialist for content strategy, technical SEO, keyword research, and ranking improvements. Use when optimizing website content, improving search rankings, conducting keyword analysis, or implementing SEO best practices. Expert in on-page SEO, meta tags, schema markup, and Core Web Vitals.

3381

seo-content-writer

sickn33

Writes SEO-optimized content based on provided keywords and topic briefs. Creates engaging, comprehensive content following best practices. Use PROACTIVELY for content creation tasks.

2477

executing-marketing-campaigns

davila7

Plans, creates, and optimizes marketing campaigns including content strategy, social media, email, and analytics. Helps develop go-to-market strategies, campaign messaging, and performance measurement.

1181

content-creation

anthropics

Draft marketing content across channels — blog posts, social media, email newsletters, landing pages, press releases, and case studies. Use when writing any marketing content, when you need channel-specific formatting, SEO-optimized copy, headline options, or calls to action.

2254

social-content

davila7

When the user wants help creating, scheduling, or optimizing social media content for LinkedIn, Twitter/X, Instagram, TikTok, Facebook, or other platforms. Also use when the user mentions 'LinkedIn post,' 'Twitter thread,' 'social media,' 'content calendar,' 'social scheduling,' 'engagement,' or 'viral content.' This skill covers content creation, repurposing, and platform-specific strategies.

2056

Search skills

Search the agent skills registry