AI-powered content creation, publishing, and social scheduling CLI. Built as a portable OpenClaw skill.
ContentAI handles the full content lifecycle: voice profiling → idea generation → drafting → QA → hero images → publishing → social copy → scheduling. All from the command line, all configurable, no hardcoded anything.
go install github.com/MikeS071/contentai@latestOr build from source:
git clone https://github.com/MikeS071/contentai.git
cd contentai
go build -o contentai .contentai install --openclawThis copies SKILL.md and workspace snippets into your OpenClaw workspace.
# 1. Create a workspace
mkdir my-content && cd my-content
# 2. Initialize — guided voice discovery + config
contentai init
# 3. Add knowledge sources (optional)
contentai kb add-feed https://simonwillison.net/atom/everything
contentai kb sync
# 4. Generate article ideas
contentai ideas --count 5
# 5. Create a new content item
contentai new "my-first-article" --title "My First Article"
# 6. Draft the article
contentai draft "my-first-article"
# 7. Run QA checks
contentai qa "my-first-article"
# 8. Generate hero image
contentai hero "my-first-article"
# 9. Publish
contentai publish "my-first-article" --approve
# 10. Generate social copy + schedule
contentai social "my-first-article"
contentai schedule "my-first-article"| Command | Description |
|---|---|
init |
Guided setup — Perspective Architect voice discovery, generates voice.md + blueprint.md |
kb add-feed <url> |
Add RSS/Atom feed to knowledge base |
kb add-note <file> |
Add markdown note to knowledge base |
kb sync |
Fetch latest articles from all feeds |
kb search <query> |
Search knowledge base |
ideas |
Generate article ideas from voice + blueprint + KB |
new <slug> |
Create content/<slug>/ with metadata scaffold |
draft <slug> |
Write article using Creative Thought Partner + Blog Writer prompts |
qa <slug> |
Run 7 quality checks, optional LLM auto-fix |
hero <slug> |
Generate hero image with 8 rotating color palettes |
publish <slug> |
Push to configured publisher (HTTP or static) |
social <slug> |
Generate platform-specific social copy (X + LinkedIn) |
schedule <slug> |
Queue for next available posting slot |
post <slug> |
Fire a scheduled post immediately |
list |
Show all content items with status |
templates export |
Export embedded prompts for local customization |
install |
Install OpenClaw skill assets to workspace |
ContentAI uses contentai.toml in the working directory:
[project]
name = "my-blog"
[llm]
provider = "openai" # openai | anthropic
model = "gpt-4o-mini" # default model for ideas, QA, social
api_key_cmd = "pass show apis/openai-api-key" # shell command to get API key
[llm.draft]
model = "gpt-4o" # stronger model for article writing
[content]
dir = "content" # where content items live
[hero]
provider = "openai"
model = "gpt-image-1"
api_key_cmd = "pass show apis/openai-api-key"
width = 1200
height = 630
title_overlay = true # overlay article title on hero image
linkedin_resize = true # generate LinkedIn-sized variant
[publish]
type = "http" # http | static
url = "https://cms.example.com/api/articles"
auth_header = "Authorization"
auth_cmd = "pass show apis/blog-api-key"
auth_prefix = "Bearer "
# field_mapping = { title = "title", slug = "slug", content = "content" }
[social.x]
enabled = true
api_key_cmd = "pass show apis/x-api-key"
api_secret_cmd = "pass show apis/x-api-secret"
access_token_cmd = "pass show apis/x-access-token"
access_token_secret_cmd = "pass show apis/x-access-token-secret"
[social.linkedin]
enabled = true
access_token_cmd = "pass show apis/linkedin-access-token"
[schedule]
timezone = "UTC"
days = ["Mon", "Tue", "Wed", "Thu", "Fri"]
window_start = "09:00"
window_end = "09:30"
[qa]
auto_fix = true # attempt LLM-powered fixes
max_fix_rounds = 2 # max auto-fix iterationsAll credentials use the api_key_cmd pattern — ContentAI shells out to your command and reads stdout. This works with:
- pass:
pass show apis/openai-api-key - Environment variables:
echo $OPENAI_API_KEY - 1Password CLI:
op read op://vault/item/field - AWS Secrets Manager:
aws secretsmanager get-secret-value --secret-id my-key --query SecretString --output text
No API keys are ever stored in config files.
idea → new → draft → qa → qa_passed → hero → publish → published → social → schedule → posted
Each content item lives in content/<slug>/ with:
content/my-article/
├── meta.json # status, title, dates, category
├── article.md # the article
├── qa.json # QA results
├── hero.png # hero image (1200x630)
├── hero-linkedin.png # LinkedIn variant
└── social.json # generated social copy
ContentAI's voice system has two parts:
Your writing voice — tone, style, anti-patterns, benchmark articles. Generated during contentai init via the Perspective Architect prompt (a guided 5-phase discovery process) or written manually.
Your intellectual blueprint — core ideas, content pathways, themes. This ensures every article connects back to your broader worldview, not just the topic at hand.
Both files are included in every LLM call. The voice profile is never truncated.
ContentAI ships with 8 embedded prompts:
| Template | Used by |
|---|---|
perspective-architect |
init — guided voice discovery |
voice-extractor |
init — extract voice from example articles |
deep-post-ideas |
ideas — generate article outlines |
creative-thought-partner |
draft — develop the article angle |
blog-writer |
draft — write the article |
qa-checklist |
qa — quality check rules |
hero-prompt |
hero — image generation prompt |
social-copy |
social — platform-specific copy |
Export and customize:
contentai templates export # writes to templates/ directory
# Edit templates/*.md to your liking
# ContentAI uses local templates when present, falls back to embeddedThe qa command runs 7 built-in checks:
- no_secrets — scans for API keys, tokens, passwords
- voice_consistency — checks article against voice.md rules
- accuracy — flags unsupported claims and vague statements
- dash_cleanup — catches unnecessary mid-sentence dashes
- dedup — detects repeated phrases and restated points
- reading_level — flags overly long sentences
- length — validates word count (default 500-1000)
With auto_fix = true, ContentAI uses the LLM to propose fixes and shows a diff for each. Fixes are applied only with confirmation or --auto-approve.
Posts to any REST API. Configure URL, auth, and field mapping in contentai.toml.
Writes formatted output to a local directory. Useful for static site generators.
Implement the publisher interface in Go and register it. The adapter pattern is open for community additions.
OAuth 1.0a authentication. Generates tweet-length copy with optional thread support.
OAuth 2.0 authentication. Generates professional-format posts.
Both adapters respect the hard rule: no auto-posting without explicit approval, ever. This is not configurable.
- No auto-posting — social posts always require explicit
--approveorcontentai post. Never fires automatically. - QA before publish —
publishrequires QA pass (skip with--skip-qaif you know what you're doing). - No secrets in config — all credentials via
api_key_cmdshell commands. - Voice is sacred —
voice.mdis included in full in every LLM call, never truncated or summarized.
# Run tests
go test ./...
# Run tests with coverage
go test ./... -cover
# Build
go build -o contentai .MIT