Crier
Cross-post your content to dev.to, Hashnode, Medium, Bluesky, Mastodon, and more
Install / Use
/learn @queelius/CrierREADME
Crier
Cross-post your content to dev.to, Ghost, WordPress, Hashnode, Medium, Bluesky, Mastodon, Threads, Telegram, Discord, and more.
Like a town crier announcing your content to the world.
Getting Started
Quick Setup
pip install crier
cd your-blog
crier init
The init command walks you through:
- Creating the
.crier/registry directory - Detecting your content directories
- Configuring platforms with API keys
How It Works
- Your markdown posts with YAML front matter are the source of truth
.crier/registry.yamltracks what's published wherecrier auditshows what's missing or changedcrier publishoraudit --publishpublishes content
# See what needs publishing
crier audit
# Publish a file to a platform
crier publish post.md --to devto
# Publish to multiple platforms
crier publish post.md --to devto --to bluesky --to mastodon
# Bulk publish missing content (interactive)
crier audit --publish
With Claude Code
Crier is designed to work with Claude Code for AI-assisted publishing.
Install the skill with crier skill install, then just ask Claude naturally:
- "Cross-post my latest article to all platforms"
- "What articles haven't been published to Bluesky?"
- "Publish this post to Mastodon with a good announcement"
Claude automatically detects when to use the crier skill and follows the workflow: audit, select, publish (with rewrites for short-form platforms).
Installation
pip install crier
Supported Platforms
| Platform | API Key Format | Notes |
|----------|---------------|-------|
| dev.to | api_key | Full article support |
| Hashnode | token or token:publication_id | Full article support |
| Medium | integration_token | Publish only (no edit/list) |
| Ghost | https://site.com:key_id:key_secret | Full article support |
| WordPress | site.wordpress.com:token or https://site.com:user:app_pass | Full article support |
| Buttondown | api_key | Newsletter publishing |
| Bluesky | handle:app_password | Short posts with link cards |
| Mastodon | instance:access_token | Toots with hashtags |
| Threads | user_id:access_token | Short posts (no edit support) |
| Telegram | bot_token:chat_id | Channel/group posts |
| Discord | webhook_url | Server announcements |
| LinkedIn | access_token | Requires API access |
| Twitter/X | any (copy-paste mode) | Generates tweet for manual posting |
Platform Notes
Blog Platforms (dev.to, Hashnode, Medium, Ghost, WordPress):
- Full markdown article publishing
- Preserves front matter (title, description, tags, canonical_url)
- Best for long-form content
Newsletter Platforms (Buttondown):
- Publishes to email subscribers
- Full markdown support
- Great for content repurposing
Social Platforms (Bluesky, Mastodon, LinkedIn, Twitter, Threads):
- Creates short posts with link to canonical URL
- Uses title + description + hashtags from tags
- Best for announcing new content
Announcement Channels (Telegram, Discord):
- Posts to channels/servers
- Good for community announcements
- Discord uses webhook embeds
Manual Mode
For platforms with restrictive API access (Medium, LinkedIn, Twitter/X), you can use manual (copy-paste) mode:
# Explicit manual mode with --manual flag
crier publish post.md --to medium --manual
crier publish post.md --to linkedin --manual
# Skip auto-opening browser
crier publish post.md --to twitter --manual --no-browser
Auto-manual mode: If you configure a platform's API key to "manual", crier automatically uses manual mode:
# Configure platform for manual mode (no API key needed)
crier config set twitter.api_key manual
crier config set linkedin.api_key manual
# Now these automatically use manual mode without --manual flag
crier publish post.md --to twitter
crier publish post.md --to linkedin
Manual mode workflow:
- Formats content for the platform
- Copies it to your clipboard
- Opens the compose page in your browser
- Asks if you successfully posted
- Records to registry only if you confirm
This ensures the registry accurately reflects what's actually published.
Configuration
Crier uses two configuration files:
Global Config (~/.config/crier/config.yaml)
API keys and profiles (shared across all projects):
platforms:
devto:
api_key: your_key_here
bluesky:
api_key: "handle.bsky.social:app-password"
mastodon:
api_key: "mastodon.social:access-token"
twitter:
api_key: manual # Copy-paste mode
medium:
api_key: import # URL import mode
profiles:
blogs:
- devto
- hashnode
- medium
social:
- bluesky
- mastodon
everything:
- blogs # Profiles can reference other profiles
- social
Local Config (.crier/config.yaml)
Project-specific settings:
content_paths:
- content # Directories to scan for markdown files
site_base_url: https://yoursite.com
exclude_patterns:
- _index.md # Files to skip (Hugo section pages)
file_extensions:
- .md
- .mdx # Optional: for MDX content
default_profile: everything # Used when no --to or --profile specified
rewrite_author: claude-code # Default author for AI-generated rewrites
| Option | Purpose |
|--------|---------|
| content_paths | Directories to scan for content |
| site_base_url | For inferring canonical URLs |
| exclude_patterns | Filename patterns to skip |
| file_extensions | Extensions to scan (default: .md) |
| default_profile | Default platforms when none specified |
| rewrite_author | Default --rewrite-author value |
Environment Variables
Environment variables override config files:
export CRIER_DEVTO_API_KEY=your_key_here
export CRIER_BLUESKY_API_KEY="handle.bsky.social:app-password"
Markdown Format
Crier reads standard markdown with YAML or TOML front matter:
---
title: "My Amazing Post"
description: "A brief description"
tags: [python, programming]
canonical_url: https://myblog.com/my-post
published: true
---
Your content here...
TOML front matter is also supported (delimited by +++):
+++
title = "My Amazing Post"
description = "A brief description"
tags = ["python", "programming"]
[extra]
canonical_url = "https://myblog.com/my-post"
+++
Your content here...
Commands
# Publishing
crier init # Interactive setup wizard
crier publish FILE --to PLATFORM # Publish to platform(s)
crier publish FILE --to PLATFORM --manual # Manual copy-paste mode
crier publish FILE --to bluesky --thread # Publish as thread
crier audit # See what's missing/changed
crier audit --publish # Bulk publish interactively
crier audit --publish --yes # Bulk publish without prompting
# Content Management
crier search # List all content
crier search --tag python --since 1w # Filter by tag and date
crier status [FILE] # Show publication status
crier list PLATFORM # List your articles
crier delete FILE --from PLATFORM # Delete from platform
crier archive FILE # Archive (exclude from audit)
crier unarchive FILE # Unarchive
# Scheduling
crier schedule list # List scheduled posts
crier schedule show ID # Show scheduled post details
crier schedule cancel ID # Cancel scheduled post
crier schedule run # Publish due posts
# Analytics
crier stats # Show stats for all content
crier stats FILE # Show stats for specific file
crier stats --top 10 # Top 10 by engagement
crier stats --refresh # Refresh from platforms
# Configuration
crier config show # Show configuration
crier config set KEY VALUE # Set configuration
crier config llm show # Show LLM configuration
crier config llm test # Test LLM connection
crier doctor # Verify API keys work
crier skill install # Install Claude Code skill
Automation
Batch Mode
Use --batch for fully automated, non-interactive publishing (CI/CD):
# Batch mode implies --yes --json, skips manual/import platforms
crier publish post.md --to devto --to bluesky --batch
crier audit --publish --batch --long-form
Batch mode behavior:
- Implies
--yes(no confirmation prompts) - Implies
--json(structured output for parsing) - Implies
--only-api(skips manual/import platforms that require user interaction)
JSON Output
Use --json for machine-readable output:
crier publish post.md --to devto --json
crier audit --json
JSON output structure:
{
"command": "publish",
"file": "post.md",
"results": [{"platform": "devto", "success": true, "url": "..."}],
"summary": {"succeeded": 1, "failed": 0, "skipped": 0}
}
Auto-Rewrite
Use --auto-rewrite to generate short-form content using an LLM:
crier publish post.md --to bluesky --auto-rewrite
Simplest setup: If you have OPENAI_API_KEY set, it just works (defaults to gpt-4o-mini).
Or configure in ~/.config/crier/config.yaml:
# Minimal - just the API key (defaults to OpenAI + gpt-4o-mini)
llm:
api_key: sk-...
# Or full config for Ollama/other providers
llm:
base_url: http://localhost:11434/v1 # Ollama
model: llama3
# api_key: not needed for local Ollama
Environment variables (override config):
OPENAI_API_KEY— API key (auto-defaults to OpenAI endpoint + gpt-4o-mini)OPENAI_BASE_URL— Custom endpoint (e.g.,http://localhost:11434/v1for Ollama)
Bul
Related Skills
qqbot-channel
347.2kQQ 频道管理技能。查询频道列表、子频道、成员、发帖、公告、日程等操作。使用 qqbot_channel_api 工具代理 QQ 开放平台 HTTP 接口,自动处理 Token 鉴权。当用户需要查看频道、管理子频道、查询成员、发布帖子/公告/日程时使用。
claude-opus-4-5-migration
108.0kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
docs-writer
100.1k`docs-writer` skill instructions As an expert technical writer and editor for the Gemini CLI project, you produce accurate, clear, and consistent documentation. When asked to write, edit, or revie
model-usage
347.2kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
