Cross-post blog content to social platforms. Claude handles audit, user selection, and generates rewrites for short-form platforms. Invoke from ANY repo — crier works globally via site_root config.
From criernpx claudepluginhub queelius/claude-anvil --plugin crierThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides agent creation for Claude Code plugins with file templates, frontmatter specs (name, description, model), triggering examples, system prompts, and best practices.
Crier cross-posts blog content to multiple platforms. The blog is the canonical source of truth.
Division of labor:
| Platform | Mode | Limit | Updates? | Notes |
|---|---|---|---|---|
| devto | API | ∞ | Yes | Tags auto-sanitized (no hyphens) |
| hashnode | API | ∞ | Yes | |
| bluesky | API | 300 | No | Short-form, needs rewrite |
| mastodon | API | 500 | Yes | Short-form, needs rewrite |
| medium | import | ∞ | No | User imports from canonical URL |
| paste | 280 | No | Short-form, copy-paste | |
| threads | paste | 500 | No | Short-form, copy-paste |
| paste | ∞ | No | Copy-paste |
Modes:
API = automatic posting via APIimport = user imports from canonical URL (like Medium)paste = user copy-pastes (content goes to clipboard)Crier uses a single global config file. No local config, no --project flag.
~/.config/crier/config.yaml)# Where the content project lives (for registry + resolving relative content_paths)
site_root: ~/github/repos/metafunctor
# Content discovery
content_paths:
- content/post
site_base_url: https://metafunctor.com
exclude_patterns:
- _index.md
file_extensions:
- .md
# Defaults
default_profile: everything
rewrite_author: claude-code
# API keys
platforms:
bluesky:
api_key: handle:app-password
devto:
api_key: ...
# Publishing profiles
profiles:
blogs:
- devto
- hashnode
- medium
social:
- bluesky
- mastodon
everything:
- blogs
- social
Key points:
site_root tells crier where the content project and registry live<site_root>/.crier/registry.yamlsite_rootcd to the projectCRIER_CONFIG env varCRIER_{PLATFORM}_API_KEY env varsBefore cross-posting, check what's already published:
# Single file status
crier status content/post/2026-02-13-slug/index.md
# What needs publishing
crier audit # All content
crier audit content/post # Just posts
crier audit --since 1w # Last week
# Site-wide registry overview (no API calls)
crier summary
crier summary --json
# Check API keys are working
crier doctor
The common pattern: you're working in a project repo (not the blog repo), write a blog post about it, then cross-post. Crier works globally via site_root, so no need to switch directories.
1. Write/update blog post in metafunctor repo
2. Run audit to see what needs posting
3. Publish to long-form platforms (devto, hashnode) from the canonical file
4. Write short-form rewrites and publish (bluesky, mastodon) using --rewrite
5. Verify audit is clean
CRITICAL: Always publish from the canonical content file. Never create temp files for different platforms. The registry tracks publications by source file path. Publishing from temp files breaks audit tracking.
# 1. Check what needs posting
crier audit content/post/2026-02-13-my-article/index.md
# 2. Publish to long-form (uses the actual content file)
crier publish content/post/2026-02-13-my-article/index.md --to devto
crier publish content/post/2026-02-13-my-article/index.md --to hashnode
# 3. For short-form, Claude writes rewrites and publishes via --rewrite
# ALWAYS use --rewrite on the CANONICAL file — never publish temp files
crier publish content/post/2026-02-13-my-article/index.md --to bluesky \
--rewrite "Hook text about the interesting insight" --rewrite-author "claude-code"
crier publish content/post/2026-02-13-my-article/index.md --to mastodon \
--rewrite "Slightly longer summary with #hashtags" --rewrite-author "claude-code"
# 4. Verify audit is clean
crier audit content/post/2026-02-13-my-article/index.md
Never use temp files. The --rewrite flag exists precisely so you can publish platform-specific content while keeping the registry pointed at the canonical source file. If you write to /tmp/claude/crier-bluesky.md and publish that, the registry records the temp path and audit breaks.
User: cross-post my new blog post to the usual platforms
Claude: I'll cross-post content/post/2026-01-04-my-article/index.md
Let me check what's needed:
[runs: crier audit content/post/2026-01-04-my-article/index.md]
Platforms to post:
- devto (API) - will auto-post
- hashnode (API) - will auto-post
- bluesky (API, 300 chars) - I'll write a summary
- mastodon (API, 500 chars) - I'll write a summary
- medium (import) - you'll import from your URL
Proceed?
User: yes
Claude: [runs API posts for devto, hashnode]
[generates summaries and posts to bluesky, mastodon with --rewrite]
Done:
- devto: https://dev.to/user/my-article-abc
- hashnode: https://user.hashnode.dev/my-article
- bluesky: posted announcement
- mastodon: posted announcement
For Medium, import from your canonical URL:
https://yourblog.com/post/2026-01-04-my-article/
Go to: https://medium.com/p/import
[runs: crier publish article.md --to medium --yes]
Did you import to Medium successfully?
User: yes
Claude: All platforms complete.
User: post this to bluesky
Claude: [runs: crier publish article.md --to bluesky]
Error: Content too long for bluesky: 5000 characters (limit: 300)
I'll write a short announcement:
"New post: Exploring the mathematics of uncertainty - how
probabilistic thinking can transform how we approach
complex problems."
(198 chars + URL = within limit)
[runs: crier publish article.md --to bluesky \
--rewrite "New post: Exploring..." --rewrite-author "claude-code"]
bluesky: https://bsky.app/profile/user/post/abc123
User: post to medium
Claude: Medium uses URL import. I'll set this up:
[runs: crier publish article.md --to medium --yes]
Import your article from this URL:
https://yourblog.com/post/2026-01-04-my-article/
Go to: https://medium.com/p/import
Did the import work?
User: yes
Claude: Registered in crier. Done!
User: share on twitter
Claude: Twitter uses copy-paste mode.
[runs: crier publish article.md --to twitter --yes]
I've copied this to your clipboard:
"New post: My Article Title - exploring key concepts...
https://yourblog.com/post/my-article/"
Go to: https://twitter.com/compose/tweet
Did you post it?
User: yes
Claude: Registered. All done!
crier audit <file> or crier audit for all contentcrier publish <file> --to <platform>--rewritecrier publish ... --to medium --yes, tell user to importcrier publish ... --to twitter --yes, tell user to pastecrier audit to see dirty markersYou ARE the rewriter. Don't use --auto-rewrite in interactive Claude Code sessions.
| Platform | Limit | Voice | Format |
|---|---|---|---|
| Bluesky | 300 | Conversational, personal | Hook + key insight. No hashtags. |
| Mastodon | 500 | Slightly more detailed | Can include 2-3 hashtags at end. |
| 280 | Punchy, provocative | Can thread for longer content. | |
| Threads | 500 | Casual, exploratory | Like talking to a friend. |
| ∞ | Professional, structured | 3-4 paragraphs, accomplishment-framed. |
--rewrite| Situation | Use |
|---|---|
| Interactive Claude session | Claude writes rewrite, passes via --rewrite |
| Batch/bulk posting (10+ items) | --auto-rewrite for throughput |
| User says "just post it" | --auto-rewrite as shortcut |
| User cares about quality | Claude writes it, shows for approval |
The crier audit command scans all directories in content_paths by default. Users often want to scope to specific content types.
Reading user intent:
| User says... | Likely scope |
|---|---|
| "last month of content", "recent content", "what needs posting" | All content (crier audit --since 1m) |
| "blog posts", "posts", "articles" | crier audit content/post |
| "projects" | crier audit content/projects |
| "papers", "research" | crier audit content/papers |
When uncertain:
crier config show to see configured content paths# Check what needs publishing
crier audit
crier audit content/post/my-article/index.md
# Publish to API platform
crier publish article.md --to devto
# Publish with rewrite for short-form
crier publish article.md --to bluesky \
--rewrite "Short announcement text" \
--rewrite-author "claude-code"
# Import/paste mode (skips interactive prompts)
crier publish article.md --to medium --yes
crier publish article.md --to twitter --yes
# Registry summary (no API calls)
crier summary
crier summary --json
# Check API keys
crier doctor
# Manual registry management
crier register <file> --platform <platform> [--url <url>]
crier unregister <file> --platform <platform>
# Link content file to registry (fix source_file after temp-file publishing)
crier link <file> --url <canonical_url>
crier link <file> # uses canonical_url from front matter
For large content libraries, use filters to control scope:
# Post 5 random articles to blog platforms (fully automated)
crier audit --publish --yes --only-api --long-form --sample 5
# All missing to API platforms (skips manual/import)
crier audit --publish --yes --only-api
# Include updates to changed content
crier audit --publish --yes --include-changed
# Filter by path - only posts (not projects, papers, etc.)
crier audit content/post --publish --yes --only-api --long-form --sample 5
# Posts from last week
crier audit --since 1w --publish --yes --only-api
# Sample 5 recent posts (last month)
crier audit --sample 5 --since 1m --only-api --long-form
# Fully automated batch mode (implies --yes --json, skips manual)
crier audit --publish --batch --long-form
[PATH] — Limit to specific directory (e.g., content/post, content/projects)--since — Only content from this date (e.g., 1d, 1w, 1m, 2025-01-01)--until — Only content until this date--only-api — Skip manual/import/paste platforms--long-form — Skip short-form platforms (bluesky, mastodon, twitter, threads)--sample N — Random sample of N items--include-changed — Also update changed content (default: missing only)---
title: "Your Article Title"
canonical_url: "https://yourblog.com/your-article/"
---
The canonical_url is required — it's the article's identity for tracking and linking.
Crier automatically tracks the content section (e.g., post, papers, projects) for each registered article, inferred from the source file path. This powers the crier summary breakdown by section. No configuration needed.