Skip to main content

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.

Python License Status PyPI

A production-quality Python CLI that goes from topicmarkdown 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-export escape 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 — generate n varied title candidates (list / how-to / question / contrarian angles)
  • scribe outline — produce a structured [{section, description}, ...] outline
  • scribe generate — full pipeline: title → outline → draft → export to Markdown with YAML frontmatter
  • scribe seo — real computed score (readability 40% / keyword density 30% / structure 30%) + AI meta description + 3 suggestions; keywords auto-detected via NLTK when --keywords is omitted
  • scribe improve — rewrite for clarity, flow, and engagement; renders a unified diff
  • scribe tone <professional|casual|witty|technical|persuasive> — tone-shift rewrites with a Typer enum
  • scribe social — LinkedIn post, X/Twitter thread (1/, 2/, ...), Facebook post — three Rich panels; --save writes each to output/social/
  • scribe pdf — export a Markdown post to a clean, single-column PDF
  • scribe import-url — extract an article with newspaper4k and run improve, social, or seo on it
  • scribe 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 improve and tone
  • 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

scribe_gen-0.1.0.tar.gz (48.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

scribe_gen-0.1.0-py3-none-any.whl (28.3 kB view details)

Uploaded Python 3

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

Hashes for scribe_gen-0.1.0.tar.gz
Algorithm Hash digest
SHA256 755325be51bdbc0d47bdb31a4218b10851aafa067c02095f77cc35a5da432ab8
MD5 0034ed902d849da7c3a178a5ce292108
BLAKE2b-256 5a39a2db938df4faf39ade37c415caa71d597df96d2fadc2ba3ba90264d65039

See more details on using hashes here.

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

Hashes for scribe_gen-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0d40f765a31888e5e84493445b2a822b1eb7dbc389fa8b7259553242c0dc6554
MD5 84208f973f148934c6ebab08fcd1773a
BLAKE2b-256 614e2f14f3dec0b828e4caa2b0ed5b1122190a65121c2ecc7b2aeab36ac8f43e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page