AI-powered CLI for blog generation, SEO scoring, and content repurposing
Project description
SCRIBE
AI Content Generation Engine — turn a single topic into a publish-ready blog post, then repurpose it for SEO and social media.
A production-quality Python CLI that goes from topic → markdown in one command, with built-in SEO scoring, tone rewrites, and social-media repurposing. Built to demonstrate clean architecture, prompt engineering discipline, and UX polish.
Why this exists
Most "AI blog generator" demos stop at "ask the model for a post." SCRIBE goes further:
- Real SEO scoring, not AI-hallucinated numbers — readability via
textstat, keyword density from actual counts, structure heuristics. The model only fills the qualitative gaps. - Single-responsibility modules behind one API wrapper, so swapping OpenAI for Anthropic is a one-file change.
- A real CLI with Rich panels, spinners, tables, and a
--no-exportescape hatch — not a notebook. - Typed tone rewrites with a Typer enum that rejects bad input before the API call.
It's a portfolio piece: the code is the resume.
Features
scribe title— generatenvaried title candidates (list / how-to / question / contrarian angles)scribe outline— produce a structured[{section, description}, ...]outlinescribe generate— full pipeline: title → outline → draft → export to Markdown with YAML frontmatterscribe seo— real computed score (readability 40% / keyword density 30% / structure 30%) + AI meta description + 3 suggestions; keywords auto-detected via NLTK when--keywordsis omittedscribe improve— rewrite for clarity, flow, and engagement; renders a unified diffscribe tone <professional|casual|witty|technical|persuasive>— tone-shift rewrites with a Typer enumscribe social— LinkedIn post, X/Twitter thread (1/,2/, ...), Facebook post — three Rich panels;--savewrites each tooutput/social/scribe pdf— export a Markdown post to a clean, single-column PDFscribe import-url— extract an article withnewspaper4kand runimprove,social, orseoon itscribe history— Rich table of past generations- Friendly errors (Rich panels, no stack traces) for missing keys, empty topics, bad tones, API hiccups
Installation
From PyPI (recommended)
pip install scribe-gen
From source
git clone https://github.com/khizerarain/scribe.git
cd scribe
pip install -e .
Create a .env file in your working directory and add your OpenAI key:
cp .env.example .env
# edit .env and set OPENAI_API_KEY
Windows users: set PYTHONIOENCODING=utf-8 once per shell to keep Typer's Rich help (it uses →) from crashing the legacy cp1252 stdout.
Usage
scribe title --topic "AI startup ideas" --count 5
scribe outline --topic "AI startup ideas" --sections 6
scribe generate --topic "AI startup ideas" --tone casual --length 1500
scribe generate --topic "AI startup ideas" --no-export # print only
scribe seo --file output/ai-startup-ideas.md --keywords "AI startup, founder"
scribe improve --file output/ai-startup-ideas.md
scribe tone casual --file output/ai-startup-ideas.md
scribe tone technical --text "Paste draft here..."
scribe social --file output/ai-startup-ideas.md
scribe social --file output/ai-startup-ideas.md --save # write to output/social/
scribe pdf --file output/ai-startup-ideas.md --output outputs/
scribe import-url https://example.com/article --action social
scribe history
scribe --version
If you run scribe with no args you get a small ASCII banner and a quick-start hint.
Example output
____ ___ ____ ___ ___ __ __
/ ___|| _ \/ ___|_ _|_ _|| \/ |
\___ \| _/\___ \| | | | | |\/| |
___) | | ___) | | | | | | | |
|____/|_| |____/___|___| |_| |_|
SCRIBE v0.1.0 · AI content generation engine
Architecture
Every module has one job. Every API call goes through client.py so swapping providers is a single-file change.
| Module | Responsibility |
|---|---|
cli.py |
Typer app, command registration, banner, error panels |
client.py |
OpenAI wrapper (chat_completion() w/ exponential backoff) — the only module that talks to the API |
prompts.py |
All system/user prompt templates — no API calls live here |
generator.py |
generate_titles(), generate_draft() |
outlines.py |
generate_outline() |
seo.py |
analyze_seo() — computed score + AI-suggested meta/suggestions |
improver.py |
improve_content(), change_tone() |
social.py |
generate_social_posts() → {linkedin, twitter_thread, facebook} |
exporter.py |
export_markdown() — YAML frontmatter + slugified filename |
config.py |
Env loading, defaults, paths |
utils.py |
Shared Rich console, with_spinner(), slugify, history I/O |
SEO scoring formula
score = round(
readability_score * 0.40
+ keyword_score * 0.30 (skipped if no --keywords; weight redistributed)
+ structure_score * 0.30
)
- Readability (40%):
textstat.flesch_reading_ease(text)— clamped to 0-100 - Keyword density (30%): per-keyword
count / total_words * 100; 1-2% ideal, 0.5-1% or 2-3% acceptable, <0.5% low, >3% stuffed - Structure (30%): H2 headings + intro paragraph + conclusion section + paragraph-length distribution
The AI is asked for exactly two qualitative outputs: a meta description (150-160 chars) and three concrete improvement suggestions. It is never asked for the score.
Configuration
| Env var | Default | Purpose |
|---|---|---|
OPENAI_API_KEY |
(required) | Your OpenAI API key |
SCRIBE_MODEL |
gpt-4o-mini |
Any chat-completions-compatible model |
.env is gitignored. If the key is missing, SCRIBE prints a Rich error panel and exits 1 — never a stack trace.
Error handling
| Situation | Behavior |
|---|---|
Missing OPENAI_API_KEY |
Rich red panel with setup steps, exit 1 |
Empty / too-short --topic |
Friendly yellow panel asking for more detail, exit 1 |
Invalid --tone |
Typer auto-rejects with valid options listed, exit 2 |
| OpenAI rate-limit / network error | Retried 3× with exponential backoff (client.py); clear Rich panel if all fail |
| AI returns malformed JSON for SEO suggestions | Falls back to a deterministic truncated meta; the score (computed) is unaffected |
Roadmap (v2 — seams are in place, not implemented)
- SQLite history with full-text search, tags, and per-topic stats
- LangChain orchestration for prompt chaining and tool use
- Vector DB style-matching — pull the top 3 past posts in a similar tone and use them as few-shot examples
- Template library — load user-defined prompt templates from disk
- Streaming responses for live diff rendering in
improveandtone - Concurrent social generation with bounded concurrency (3 calls today run sequentially)
Development
# Verify the CLI
python -m scribe.cli --help
python -m scribe.cli --version
# Manual smoke test of a single command without hitting the API
PYTHONIOENCODING=utf-8 python -m scribe.cli seo --file output/<some>.md
The v2 hook: comments in config.py, prompts.py, and client.py mark the seams where new pieces slot in.
License
MIT — see LICENSE.
Sample output
The .gitignore excludes output/*.md by default so generated content doesn't pollute commits. To produce a real screenshot for the README, run something like:
scribe generate --topic "Starting an AI business in 2026" --tone professional --length 1500
scribe seo --file output/starting-an-ai-business-in-2026.md --keywords "AI business"
Then commit the resulting .md file manually with git add -f output/<file>.md for the portfolio screenshot.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scribe_gen-0.1.0.tar.gz.
File metadata
- Download URL: scribe_gen-0.1.0.tar.gz
- Upload date:
- Size: 48.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
755325be51bdbc0d47bdb31a4218b10851aafa067c02095f77cc35a5da432ab8
|
|
| MD5 |
0034ed902d849da7c3a178a5ce292108
|
|
| BLAKE2b-256 |
5a39a2db938df4faf39ade37c415caa71d597df96d2fadc2ba3ba90264d65039
|
File details
Details for the file scribe_gen-0.1.0-py3-none-any.whl.
File metadata
- Download URL: scribe_gen-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0d40f765a31888e5e84493445b2a822b1eb7dbc389fa8b7259553242c0dc6554
|
|
| MD5 |
84208f973f148934c6ebab08fcd1773a
|
|
| BLAKE2b-256 |
614e2f14f3dec0b828e4caa2b0ed5b1122190a65121c2ecc7b2aeab36ac8f43e
|