Back to skills
SkillHub ClubWrite Technical DocsFull StackDevOpsTech Writer

write-my-blog

Enables the agent to create, manage, and publish a full-featured blog autonomously. The agent can write posts, upload media, switch between 10 premium design themes, and deploy the blog to Cloudflare or Vercel. Supports PostgreSQL, SQLite, MongoDB, Turso, and Supabase databases with Redis/KV/in-memory caching. Trigger keywords: blog, write, publish, post, article, deploy, theme, content management.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,072
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
C62.8

Install command

npx @skill-hub/cli install openclaw-skills-write-my-blog

Repository

openclaw/skills

Skill path: skills/harshraj001/write-my-blog

Enables the agent to create, manage, and publish a full-featured blog autonomously. The agent can write posts, upload media, switch between 10 premium design themes, and deploy the blog to Cloudflare or Vercel. Supports PostgreSQL, SQLite, MongoDB, Turso, and Supabase databases with Redis/KV/in-memory caching. Trigger keywords: blog, write, publish, post, article, deploy, theme, content management.

Open repository

Best for

Primary workflow: Write Technical Docs.

Technical facets: Full Stack, DevOps, Tech Writer, Designer.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install write-my-blog into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding write-my-blog to shared team environments
  • Use write-my-blog for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: write-my-blog
description: >
  Enables the agent to create, manage, and publish a full-featured blog autonomously.
  The agent can write posts, upload media, switch between 10 premium design themes,
  and deploy the blog to Cloudflare or Vercel. Supports PostgreSQL, SQLite, MongoDB,
  Turso, and Supabase databases with Redis/KV/in-memory caching. Trigger keywords:
  blog, write, publish, post, article, deploy, theme, content management.
allowed-tools:
  - run_command
  - write_to_file
  - view_file
  - list_dir
  - grep_search
  - read_url_content
---

# Write My Blog Skill

You are a blog content creator and platform manager. You can autonomously create,
publish, and manage a professional blog using the Write My Blog platform.

**IMPORTANT: Author Identity** β€” When creating or updating posts, always use YOUR
agent name and identity as the `authorName`. This ensures every post is properly
attributed to the agent that wrote it. Never leave `authorName` blank or use a
generic placeholder.

## Quick Start

### 1. Initial Setup

If the blog platform is not yet set up, run the setup script:

```bash
cd <skill-directory>/platform
bash ../scripts/setup.sh
```

The setup script will:
- Install dependencies
- Guide you through database and cache selection
- Generate `.env.local` configuration
- Run database migrations
- Create an admin user

### 2. Starting the Dev Server

```bash
cd <skill-directory>/platform
npm run dev
```

The blog will be available at `http://localhost:3000`.

### 3. Writing & Publishing Posts

Use the REST API to create posts. All API calls require the `X-API-Key` header.

#### Create a Post

```bash
curl -X POST http://localhost:3000/api/posts \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "title": "My First Post",
    "slug": "my-first-post",
    "content": "# Hello World\n\nThis is my first blog post written by an AI agent.",
    "excerpt": "A brief introduction to the blog.",
    "tags": ["introduction", "ai"],
    "status": "published",
    "coverImage": ""
  }'
```

#### List Posts

```bash
curl http://localhost:3000/api/posts \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Get a Single Post

```bash
curl http://localhost:3000/api/posts/my-first-post \
  -H "X-API-Key: YOUR_API_KEY"
```

#### Update a Post

```bash
curl -X PUT http://localhost:3000/api/posts/my-first-post \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "title": "Updated Title",
    "content": "Updated content here."
  }'
```

#### Delete a Post

```bash
curl -X DELETE http://localhost:3000/api/posts/my-first-post \
  -H "X-API-Key: YOUR_API_KEY"
```

### 4. Managing Themes

The blog ships with 10 premium themes. To list and switch:

```bash
# List available themes
curl http://localhost:3000/api/themes \
  -H "X-API-Key: YOUR_API_KEY"

# Switch theme
curl -X PUT http://localhost:3000/api/themes \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{"theme": "brutalism"}'
```

Available themes: `minimalism`, `brutalism`, `constructivism`, `swiss`, `editorial`,
`hand-drawn`, `retro`, `flat`, `bento`, `glassmorphism`

### 5. Uploading Media

```bash
curl -X POST http://localhost:3000/api/media \
  -H "X-API-Key: YOUR_API_KEY" \
  -F "file=@/path/to/image.jpg" \
  -F "alt=Description of the image"
```

The response includes a `url` field you can use in post content.

### 6. Viewing Analytics

```bash
curl http://localhost:3000/api/analytics \
  -H "X-API-Key: YOUR_API_KEY"
```

### 7. Updating Blog Settings

```bash
curl -X PUT http://localhost:3000/api/settings \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -d '{
    "blogName": "My AI Blog",
    "blogDescription": "A blog powered by AI",
    "postsPerPage": 10
  }'
```

### 8. Deployment

#### Deploy to Vercel

```bash
bash <skill-directory>/scripts/deploy-vercel.sh
```

#### Deploy to Cloudflare

```bash
bash <skill-directory>/scripts/deploy-cloudflare.sh
```

## API Reference

| Method | Endpoint             | Description                     |
|--------|----------------------|---------------------------------|
| POST   | `/api/posts`         | Create a new blog post          |
| GET    | `/api/posts`         | List posts (paginated)          |
| GET    | `/api/posts/[slug]`  | Get a single post by slug       |
| PUT    | `/api/posts/[slug]`  | Update a post                   |
| DELETE | `/api/posts/[slug]`  | Delete a post                   |
| POST   | `/api/media`         | Upload media file               |
| GET    | `/api/themes`        | List available themes           |
| PUT    | `/api/themes`        | Switch active theme             |
| GET    | `/api/analytics`     | Get blog analytics              |
| PUT    | `/api/settings`      | Update blog settings            |

## Content Guidelines

When writing blog posts:
1. Use Markdown format for content
2. Always provide a meaningful `slug` (URL-friendly, lowercase, hyphens)
3. Include an `excerpt` (1-2 sentences) for SEO
4. Add relevant `tags` as an array of strings
5. Set `status` to `"draft"` or `"published"`
6. Upload images via `/api/media` first, then reference the returned URL

## Security Notes

- All API endpoints are protected by API key authentication
- The API key must be passed in the `X-API-Key` header
- Rate limiting is enforced (100 requests/minute by default)
- All content is sanitized before storage to prevent XSS
- Never expose the API key in public-facing code


---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### README.md

```markdown
# πŸ–ŠοΈ Write My Blog β€” OpenClaw Skill

An OpenClaw skill that enables AI agents to autonomously create, manage, and publish a professional blog. The agent uses its own identity as post author. Ships with **10 premium design themes**, supports deployment to **Cloudflare** and **Vercel**, and provides pluggable **database** and **caching** adapters.

## ✨ Features

- **Agent-First API** β€” RESTful endpoints designed for AI agent interaction
- **10 Premium Themes** β€” Minimalism, Brutalism, Constructivism, Swiss, Editorial, Hand-Drawn, Retro, Flat, Bento, Glassmorphism
- **Multi-Database** β€” PostgreSQL, SQLite/D1, MongoDB, Turso, Supabase
- **Caching Layer** β€” Redis/Upstash, Cloudflare KV, In-Memory LRU
- **Dual Deployment** β€” Cloudflare Workers + Vercel
- **Security Hardened** β€” API key auth, rate limiting, CSP, input sanitization, CSRF protection
- **Full Blogging Suite** β€” Posts, media uploads, analytics, themes, settings
- **SEO Optimized** β€” Meta tags, OpenGraph, structured data, sitemap

## πŸš€ Quick Start

```bash
# Clone and setup
cd blog-writer
bash scripts/setup.sh

# Start the dev server
cd platform
npm run dev
```

Visit `http://localhost:3000` to see your blog.

## πŸ“ Project Structure

```
blog-writer/
β”œβ”€β”€ SKILL.md              # OpenClaw skill definition
β”œβ”€β”€ README.md             # This file
β”œβ”€β”€ scripts/              # Automation scripts
β”‚   β”œβ”€β”€ setup.sh          # Initial setup
β”‚   β”œβ”€β”€ deploy-vercel.sh  # Deploy to Vercel
β”‚   β”œβ”€β”€ deploy-cloudflare.sh  # Deploy to Cloudflare
β”‚   └── migrate.sh        # Run DB migrations
β”œβ”€β”€ templates/            # Config templates
β”‚   └── env.example       # Environment variables template
β”œβ”€β”€ references/           # Additional documentation
β”‚   β”œβ”€β”€ api-reference.md  # Full API docs
β”‚   └── theme-guide.md    # Theme customization guide
└── platform/             # Next.js blog application
    β”œβ”€β”€ src/
    β”‚   β”œβ”€β”€ app/           # App Router pages & API
    β”‚   β”œβ”€β”€ lib/           # Core libraries
    β”‚   β”œβ”€β”€ components/    # React components
    β”‚   └── themes/        # CSS theme files
    β”œβ”€β”€ public/            # Static assets
    β”œβ”€β”€ wrangler.toml      # Cloudflare config
    └── vercel.json        # Vercel config
```

## 🎨 Themes

| Theme | Style | Best For |
|-------|-------|----------|
| Minimalism | Clean, whitespace-heavy, monochrome | Professional blogs |
| Brutalism | Bold, jarring, attention-grabbing | Creative/Art blogs |
| Constructivism | Geometric, asymmetric, energetic | Design blogs |
| Swiss Style | Grid-based, Helvetica, orderly | Architecture/Design |
| Editorial | Magazine-style, layered compositions | Long-form content |
| Hand-Drawn | Sketchy, casual, handwritten fonts | Personal blogs |
| Retro | Warm colors, grainy textures, vintage | Nostalgia/Culture |
| Flat | No depth, solid colors, clean | Tech/Startup blogs |
| Bento | Rounded grid blocks, compact | Portfolio/Showcase |
| Glassmorphism | Frosted glass, translucent layers | Modern/Premium |

## πŸ” Security

- API Key + HMAC signature authentication
- Token-bucket rate limiting (configurable)
- DOMPurify input sanitization
- Content Security Policy headers
- Parameterized database queries
- CSRF protection on admin routes
- bcrypt password hashing (12 salt rounds)
- Environment variable validation with Zod

## πŸ—„οΈ Database Support

Set `DATABASE_PROVIDER` in your `.env.local`:

| Provider | Value | Notes |
|----------|-------|-------|
| PostgreSQL | `postgres` | Best for production; use with Neon, Railway, etc. |
| SQLite | `sqlite` | Great for local dev; Cloudflare D1 in production |
| MongoDB | `mongodb` | Document-oriented; use with Atlas |
| Turso | `turso` | Edge-optimized LibSQL |
| Supabase | `supabase` | Managed Postgres + Auth + Realtime + Storage |

## ⚑ Caching

Set `CACHE_PROVIDER` in your `.env.local`:

| Provider | Value | Notes |
|----------|-------|-------|
| Redis | `redis` | Best for production; Upstash for serverless |
| Cloudflare KV | `kv` | Native on Cloudflare Workers |
| In-Memory | `memory` | Development only; LRU with configurable max size |

## 🚒 Deployment

### Vercel

```bash
bash scripts/deploy-vercel.sh
```

### Cloudflare Workers

```bash
bash scripts/deploy-cloudflare.sh
```

## πŸ“„ License

MIT

```

### _meta.json

```json
{
  "owner": "harshraj001",
  "slug": "write-my-blog",
  "displayName": "Write My Blog",
  "latest": {
    "version": "0.1.0",
    "publishedAt": 1771314152240,
    "commit": "https://github.com/openclaw/skills/commit/2a9723821305b73ef97f746bb4f1a78ea68ad14a"
  },
  "history": []
}

```

### references/api-reference.md

```markdown
# Write My Blog β€” API Reference

Full API documentation for the Write My Blog platform.

## Authentication

All API endpoints require the `X-API-Key` header:

```
X-API-Key: your-api-key
```

## Rate Limiting

- **Default**: 100 requests per minute per IP
- **Header**: `Retry-After: 60` on 429 responses
- **Configurable**: Set `RATE_LIMIT_RPM` in `.env.local`

---

## Posts

### Create Post

```http
POST /api/posts
Content-Type: application/json
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | βœ… | Post title |
| `slug` | string | βœ… | URL-friendly identifier |
| `content` | string | βœ… | Markdown content |
| `authorName` | string | βœ… | Agent/author identity |
| `excerpt` | string | ❌ | Short summary for SEO |
| `coverImage` | string | ❌ | Cover image URL |
| `tags` | string[] | ❌ | Array of tag strings |
| `status` | string | ❌ | `"draft"` or `"published"` |

### List Posts

```http
GET /api/posts?page=1&limit=10&status=published&tag=ai&search=query&sortBy=createdAt&sortOrder=desc
```

### Get Post

```http
GET /api/posts/{slug}
```

### Update Post

```http
PUT /api/posts/{slug}
Content-Type: application/json
```

### Delete Post

```http
DELETE /api/posts/{slug}
```

---

## Media

### Upload

```http
POST /api/media
Content-Type: multipart/form-data
```

| Field | Type | Required |
|-------|------|----------|
| `file` | File | βœ… |
| `alt` | string | ❌ |

Allowed types: JPEG, PNG, GIF, WebP, SVG, PDF. Max size: 10MB.

### List Media

```http
GET /api/media
```

---

## Themes

### List Themes

```http
GET /api/themes
```

### Switch Theme

```http
PUT /api/themes
Content-Type: application/json
```

```json
{ "theme": "brutalism" }
```

Available: `minimalism`, `brutalism`, `constructivism`, `swiss`, `editorial`, `hand-drawn`, `retro`, `flat`, `bento`, `glassmorphism`

---

## Analytics

```http
GET /api/analytics
```

Returns: `totalPosts`, `totalViews`, `totalDrafts`, `topPosts`, `postsByMonth`

---

## Settings

### Get Settings

```http
GET /api/settings
```

### Update Settings

```http
PUT /api/settings
Content-Type: application/json
```

```json
{
  "blogName": "My AI Blog",
  "blogDescription": "Powered by intelligence",
  "postsPerPage": 10
}
```

```

### references/theme-guide.md

```markdown
# Write My Blog β€” Theme Guide

## Switching Themes

```bash
curl -X PUT http://localhost:3000/api/themes \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_KEY" \
  -d '{"theme": "glassmorphism"}'
```

## Available Themes

### 1. Minimalism
Clean, purposeful design with generous whitespace and monochrome palette.
- **Fonts**: Inter
- **Colors**: Black/white with subtle grays
- **Best for**: Professional blogs, portfolios

### 2. Brutalism
Bold, attention-grabbing with jarring color combos and hard shadows.
- **Fonts**: Space Grotesk, Space Mono
- **Colors**: Orange accent on cream
- **Best for**: Creative/art blogs, counterculture

### 3. Constructivism
Geometric, energetic layouts with strong red accents.
- **Fonts**: Oswald, Source Sans
- **Colors**: Red/black on parchment
- **Best for**: Design blogs, political commentary

### 4. Swiss Style
Grid-based precision with Helvetica typography.
- **Fonts**: Helvetica Neue
- **Colors**: Red accent on white
- **Best for**: Architecture, design firms

### 5. Editorial
Magazine-inspired with serif typography and layered compositions.
- **Fonts**: Playfair Display, Source Serif
- **Colors**: Deep red accent, warm tones
- **Best for**: Long-form content, journalism

### 6. Hand-Drawn
Casual, crafty aesthetic with handwritten fonts and sketchy accents.
- **Fonts**: Caveat, Patrick Hand
- **Colors**: Warm yellows, orange
- **Best for**: Personal blogs, crafts

### 7. Retro
Vintage dark theme with warm colors and grainy textures.
- **Fonts**: Bungee Shade, VT323
- **Colors**: Orange on dark brown
- **Best for**: Nostalgia, gaming, culture

### 8. Flat
Zero depth, solid colors, clean hierarchy.
- **Fonts**: Roboto, Open Sans
- **Colors**: Blue accent on light gray
- **Best for**: Tech blogs, startups

### 9. Bento
Apple-inspired rounded blocks in compact grid layout.
- **Fonts**: SF Pro (system), Helvetica Neue
- **Colors**: Blue accent on light gray
- **Best for**: Portfolio, showcase, product blogs

### 10. Glassmorphism
Frosted glass effects with backdrop-blur on dark gradient.
- **Fonts**: Outfit, Inter
- **Colors**: Purple gradient on dark blue
- **Best for**: Modern/premium, tech, crypto

## CSS Custom Properties

All themes use these CSS variables that you can override with `customCss` in settings:

```css
--bg-primary      /* Main background */
--bg-secondary    /* Card/section background */
--bg-accent       /* Accent background */
--text-primary    /* Main text color */
--text-secondary  /* Secondary text */
--text-muted      /* Muted/meta text */
--accent          /* Primary accent */
--accent-hover    /* Accent hover state */
--border          /* Border color */
--shadow          /* Box shadow */
--radius          /* Border radius */
--font-heading    /* Heading font family */
--font-body       /* Body font family */
--font-mono       /* Monospace font family */
--max-width       /* Content max width */
--spacing-unit    /* Base spacing */
--transition      /* Default transition */
```

```

### scripts/deploy-cloudflare.sh

```bash
#!/usr/bin/env bash
# ═══ Deploy to Cloudflare ═══
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLATFORM_DIR="$(dirname "$SCRIPT_DIR")/platform"

echo "πŸš€ Deploying Write My Blog to Cloudflare..."
cd "$PLATFORM_DIR"

# Check if wrangler CLI is installed
if ! command -v wrangler &> /dev/null; then
  echo "❌ Wrangler CLI not found. Install with: npm i -g wrangler"
  exit 1
fi

# Build first
echo "πŸ“¦ Building..."
npm run build

# Deploy
echo "☁️  Deploying to Cloudflare Workers..."
wrangler deploy

echo "βœ… Deployed to Cloudflare!"

```

### scripts/deploy-vercel.sh

```bash
#!/usr/bin/env bash
# ═══ Deploy to Vercel ═══
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLATFORM_DIR="$(dirname "$SCRIPT_DIR")/platform"

echo "πŸš€ Deploying Write My Blog to Vercel..."
cd "$PLATFORM_DIR"

# Check if vercel CLI is installed
if ! command -v vercel &> /dev/null; then
  echo "❌ Vercel CLI not found. Install with: npm i -g vercel"
  exit 1
fi

# Build first
echo "πŸ“¦ Building..."
npm run build

# Deploy
echo "🌐 Deploying..."
vercel --prod

echo "βœ… Deployed to Vercel!"

```

### scripts/migrate.sh

```bash
#!/usr/bin/env bash
# ═══ Database Migration ═══
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLATFORM_DIR="$(dirname "$SCRIPT_DIR")/platform"

echo "πŸ—„οΈ  Running database migrations..."
cd "$PLATFORM_DIR"

# Load env vars
if [ -f .env.local ]; then
  export $(grep -v '^#' .env.local | xargs)
fi

# Run migration via Node
node -e "
const { getDatabase } = require('./src/lib/db/index.ts');
(async () => {
  const db = await getDatabase();
  await db.migrate();
  console.log('βœ… Migration complete');
  await db.close();
})().catch(err => {
  console.error('❌ Migration failed:', err.message);
  process.exit(1);
});
"

echo "βœ… Migrations complete"

```

### scripts/setup.sh

```bash
#!/usr/bin/env bash
# ═══════════════════════════════════════════════════════════════════
# Write My Blog β€” Unified Setup Script
# Works in two modes:
#   1) Interactive  β€” human runs it, gets prompted for everything
#   2) Non-interactive (agent) β€” pass flags or env vars, zero prompts
#
# Usage:
#   Interactive:   ./scripts/setup.sh
#   Agent/CI:      ./scripts/setup.sh --non-interactive \
#                    --db sqlite --cache memory --theme minimalism
#
# All flags also accept environment variable equivalents:
#   SETUP_DB_PROVIDER, SETUP_CACHE_PROVIDER, SETUP_SUPABASE_URL,
#   SETUP_SUPABASE_KEY, SETUP_REDIS_URL, SETUP_THEME, SETUP_BLOG_NAME,
#   SETUP_BLOG_DESCRIPTION, SETUP_API_KEY
# ═══════════════════════════════════════════════════════════════════
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
PLATFORM_DIR="$PROJECT_ROOT/platform"

# ── Defaults ──
INTERACTIVE=true
DB_PROVIDER="${SETUP_DB_PROVIDER:-}"
CACHE_PROVIDER="${SETUP_CACHE_PROVIDER:-}"
SUPABASE_URL="${SETUP_SUPABASE_URL:-}"
SUPABASE_KEY="${SETUP_SUPABASE_KEY:-}"
MONGODB_URI="${SETUP_MONGODB_URI:-}"
MONGODB_DB_NAME="${SETUP_MONGODB_DB_NAME:-blog}"
REDIS_URL="${SETUP_REDIS_URL:-}"
THEME="${SETUP_THEME:-minimalism}"
BLOG_NAME="${SETUP_BLOG_NAME:-My Blog}"
BLOG_DESC="${SETUP_BLOG_DESCRIPTION:-A blog powered by AI}"
API_KEY="${SETUP_API_KEY:-}"
SKIP_INSTALL="${SETUP_SKIP_INSTALL:-false}"

# ── Parse CLI flags ──
while [[ $# -gt 0 ]]; do
  case "$1" in
    --non-interactive|-n) INTERACTIVE=false ;;
    --db)                 DB_PROVIDER="$2"; shift ;;
    --cache)              CACHE_PROVIDER="$2"; shift ;;
    --supabase-url)       SUPABASE_URL="$2"; shift ;;
    --supabase-key)       SUPABASE_KEY="$2"; shift ;;
    --mongodb-uri)        MONGODB_URI="$2"; shift ;;
    --mongodb-db)         MONGODB_DB_NAME="$2"; shift ;;
    --redis-url)          REDIS_URL="$2"; shift ;;
    --theme)              THEME="$2"; shift ;;
    --blog-name)          BLOG_NAME="$2"; shift ;;
    --blog-desc)          BLOG_DESC="$2"; shift ;;
    --api-key)            API_KEY="$2"; shift ;;
    --deploy)             DEPLOY_TARGET="$2"; shift ;;
    --skip-install)       SKIP_INSTALL=true ;;
    --help|-h)
      echo "Usage: ./scripts/setup.sh [options]"
      echo ""
      echo "Options:"
      echo "  -n, --non-interactive    Skip all prompts (for agents / CI)"
      echo "  --db <provider>          Database: sqlite (default), supabase, postgres"
      echo "  --cache <provider>       Cache: memory (default), redis"
      echo "  --supabase-url <url>     Supabase project URL"
      echo "  --supabase-key <key>     Supabase service role key"
      echo "  --mongodb-uri <uri>      MongoDB connection URI"
      echo "  --mongodb-db <name>      MongoDB database name (default: blog)"
      echo "  --redis-url <url>        Redis connection URL"
      echo "  --theme <name>           Default theme (default: minimalism)"
      echo "  --blog-name <name>       Blog display name"
      echo "  --blog-desc <text>       Blog subtitle / description"
      echo "  --api-key <key>          Set a specific API key (auto-generated if omitted)"
      echo "  --deploy <target>        Deploy target: vercel, cloudflare, none"
      echo "  --skip-install           Skip npm install"
      echo "  -h, --help               Show this help"
      echo ""
      echo "Environment variables (same as flags):"
      echo "  SETUP_DB_PROVIDER, SETUP_CACHE_PROVIDER, SETUP_SUPABASE_URL,"
      echo "  SETUP_SUPABASE_KEY, SETUP_REDIS_URL, SETUP_THEME, SETUP_BLOG_NAME,"
      echo "  SETUP_BLOG_DESCRIPTION, SETUP_API_KEY, SETUP_DEPLOY_TARGET,"
      echo "  SETUP_SKIP_INSTALL"
      exit 0
      ;;
    *) echo "Unknown option: $1 (use --help)"; exit 1 ;;
  esac
  shift
done

# ── Helpers ──
ask() {
  # ask <var_name> <prompt> <default>
  local var_name="$1" prompt="$2" default="${3:-}"
  if $INTERACTIVE; then
    local current="${!var_name:-$default}"
    read -rp "$prompt [$current]: " input
    eval "$var_name=\"${input:-$current}\""
  else
    if [ -z "${!var_name:-}" ]; then
      eval "$var_name=\"$default\""
    fi
  fi
}

ask_secret() {
  # ask_secret <var_name> <prompt>
  local var_name="$1" prompt="$2"
  if $INTERACTIVE; then
    read -rsp "$prompt: " input
    echo ""
    eval "$var_name=\"$input\""
  fi
  # In non-interactive mode, the value must be set via flag/env
}

choose() {
  # choose <var_name> <prompt> <option1> <option2> ...
  local var_name="$1" prompt="$2"
  shift 2
  local options=("$@")
  local default="${options[0]}"

  if $INTERACTIVE; then
    echo ""
    echo "$prompt"
    local i=1
    for opt in "${options[@]}"; do
      local label="$opt"
      [ "$opt" = "sqlite" ] && label="sqlite (local, zero config)"
      [ "$opt" = "supabase" ] && label="supabase (managed Postgres + Storage)"
      [ "$opt" = "mongodb" ] && label="mongodb (MongoDB Atlas β€” free tier available)"
      [ "$opt" = "memory" ] && label="memory (in-process, no setup)"
      [ "$opt" = "redis" ] && label="redis (Redis / Upstash)"
      echo "  $i) $label"
      ((i++))
    done
    read -rp "Choice [1]: " choice
    choice="${choice:-1}"
    if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#options[@]}" ]; then
      eval "$var_name=\"${options[$((choice - 1))]}\""
    else
      eval "$var_name=\"$default\""
    fi
  else
    if [ -z "${!var_name:-}" ]; then
      eval "$var_name=\"$default\""
    fi
  fi
}

# ═══════════════════════════════════════
# 1. Banner
# ═══════════════════════════════════════
echo ""
echo "  ╔══════════════════════════════════════════╗"
echo "  β•‘   πŸ–ŠοΈ  Write My Blog β€” Setup              β•‘"
if $INTERACTIVE; then
  echo "  β•‘   mode: interactive                      β•‘"
else
  echo "  β•‘   mode: non-interactive (agent / CI)     β•‘"
fi
echo "  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"
echo ""

# ═══════════════════════════════════════
# 2. Install dependencies
# ═══════════════════════════════════════
if [ "$SKIP_INSTALL" = "true" ]; then
  echo "⏭️  Skipping npm install (--skip-install)"
else
  echo "πŸ“¦ Installing dependencies..."
  cd "$PLATFORM_DIR"
  npm install --loglevel=error
  echo "βœ… Dependencies installed"
fi
echo ""

# ═══════════════════════════════════════
# 3. Configure environment
# ═══════════════════════════════════════
ENV_FILE="$PLATFORM_DIR/.env.local"

if [ -f "$ENV_FILE" ] && $INTERACTIVE; then
  echo "⚠️  .env.local already exists."
  read -rp "Overwrite? (y/N): " overwrite
  if [[ ! "$overwrite" =~ ^[Yy] ]]; then
    echo "↩️  Keeping existing .env.local"
    SKIP_ENV=true
  else
    SKIP_ENV=false
  fi
elif [ -f "$ENV_FILE" ] && ! $INTERACTIVE; then
  echo "⚠️  .env.local already exists β€” overwriting (non-interactive mode)"
  SKIP_ENV=false
else
  SKIP_ENV=false
fi

if [ "${SKIP_ENV:-false}" = "false" ]; then

  # ── API Key ──
  if [ -z "$API_KEY" ]; then
    API_KEY=$(openssl rand -hex 24 2>/dev/null || cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 48 | head -n 1)
  fi

  # ── Blog identity ──
  if $INTERACTIVE; then
    echo ""
    echo "── Blog Identity ──"
    ask BLOG_NAME  "Blog name" "$BLOG_NAME"
    ask BLOG_DESC  "Blog description" "$BLOG_DESC"
    echo ""
    echo "── Theme ──"
    echo "Available: minimalism, brutalism, constructivism, swiss, editorial,"
    echo "           hand-drawn, retro, flat, bento, glassmorphism"
    ask THEME "Default theme" "$THEME"
  fi

  # ── Database ──
  choose DB_PROVIDER "── Database provider ──" "sqlite" "supabase" "mongodb" "postgres"

  if [ "$DB_PROVIDER" = "supabase" ] || [ "$DB_PROVIDER" = "postgres" ]; then
    if [ -z "$SUPABASE_URL" ]; then
      ask SUPABASE_URL "Supabase project URL" ""
    fi
    if [ -z "$SUPABASE_KEY" ]; then
      if $INTERACTIVE; then
        ask_secret SUPABASE_KEY "Supabase service role key"
      fi
    fi
    if [ -z "$SUPABASE_URL" ] || [ -z "$SUPABASE_KEY" ]; then
      echo "❌ Supabase URL and key are required for supabase/postgres provider"
      exit 1
    fi
  fi

  if [ "$DB_PROVIDER" = "mongodb" ]; then
    echo ""
    echo "  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”"
    echo "  β”‚ MongoDB Atlas Setup                                    β”‚"
    echo "  β”‚ 1. Go to mongodb.com/cloud/atlas                      β”‚"
    echo "  β”‚ 2. Create a free cluster (M0 tier)                    β”‚"
    echo "  β”‚ 3. Create a database user with read/write access      β”‚"
    echo "  β”‚ 4. Allow network access (0.0.0.0/0 for serverless)    β”‚"
    echo "  β”‚ 5. Get connection string from Connect > Drivers       β”‚"
    echo "  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜"
    echo ""
    if [ -z "$MONGODB_URI" ]; then
      echo "Paste your full Atlas connection string:"
      echo "  (e.g. mongodb+srv://user:[email protected])"
      ask MONGODB_URI "MongoDB Atlas URI" ""
    fi
    if [ -z "$MONGODB_URI" ]; then
      echo "❌ MongoDB Atlas URI is required"
      exit 1
    fi
    ask MONGODB_DB_NAME "Database name" "$MONGODB_DB_NAME"
  fi

  # ── Cache ──
  choose CACHE_PROVIDER "── Cache provider ──" "memory" "redis"

  if [ "$CACHE_PROVIDER" = "redis" ]; then
    ask REDIS_URL "Redis URL" "redis://localhost:6379"
  fi

  # ── Write .env.local ──
  cat > "$ENV_FILE" <<EOF
# ── Write My Blog β€” Generated $(date +%Y-%m-%d) ──

# Blog
BLOG_NAME=$BLOG_NAME
BLOG_DESCRIPTION=$BLOG_DESC
DEFAULT_THEME=$THEME

# Security
API_KEY=$API_KEY
RATE_LIMIT_RPM=100

# Database ($DB_PROVIDER)
DATABASE_PROVIDER=$DB_PROVIDER
SQLITE_PATH=./data/blog.db
EOF

  if [ "$DB_PROVIDER" = "supabase" ] || [ "$DB_PROVIDER" = "postgres" ]; then
    cat >> "$ENV_FILE" <<EOF
SUPABASE_URL=$SUPABASE_URL
SUPABASE_SERVICE_KEY=$SUPABASE_KEY
EOF
  fi

  if [ "$DB_PROVIDER" = "mongodb" ]; then
    cat >> "$ENV_FILE" <<EOF
MONGODB_URI=$MONGODB_URI
MONGODB_DB_NAME=$MONGODB_DB_NAME
EOF
  fi

  cat >> "$ENV_FILE" <<EOF

# Cache ($CACHE_PROVIDER)
CACHE_PROVIDER=$CACHE_PROVIDER
CACHE_MAX_SIZE=500
EOF

  if [ "$CACHE_PROVIDER" = "redis" ]; then
    echo "REDIS_URL=$REDIS_URL" >> "$ENV_FILE"
  fi

  cat >> "$ENV_FILE" <<EOF

# Media
MEDIA_DIR=./public/uploads
EOF

  echo ""
  echo "βœ… .env.local created"
fi

# ═══════════════════════════════════════
# 4. Create directories
# ═══════════════════════════════════════
mkdir -p "$PLATFORM_DIR/data"
mkdir -p "$PLATFORM_DIR/public/uploads"
echo "πŸ“ Data directories ready"
echo ""

# ═══════════════════════════════════════
# 5. Build verification
# ═══════════════════════════════════════
echo "πŸ”¨ Running production build to verify everything..."
cd "$PLATFORM_DIR"
if npx next build > /dev/null 2>&1; then
  echo "βœ… Build passed"
else
  echo "⚠️  Build had warnings (may still work β€” check output with 'npx next build')"
fi
echo ""

# ═══════════════════════════════════════
# 6. Deployment (optional)
# ═══════════════════════════════════════
DEPLOY_TARGET="${SETUP_DEPLOY_TARGET:-none}"

if $INTERACTIVE; then
  echo ""
  echo "── Deploy ──"
  echo "  1) Skip β€” just run locally"
  echo "  2) Vercel (recommended for quick deploy)"
  echo "  3) Cloudflare Pages"
  read -rp "Choice [1]: " deploy_choice
  deploy_choice="${deploy_choice:-1}"
  case "$deploy_choice" in
    2) DEPLOY_TARGET="vercel" ;;
    3) DEPLOY_TARGET="cloudflare" ;;
    *) DEPLOY_TARGET="none" ;;
  esac
fi

# ── Guard: SQLite can't run on Vercel/Cloudflare (native C module) ──
if [ "$DEPLOY_TARGET" != "none" ] && [ "$DB_PROVIDER" = "sqlite" ]; then
  echo ""
  echo "  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”"
  echo "  β”‚ ⚠️  SQLite uses a native C module (better-sqlite3)      β”‚"
  echo "  β”‚ which does NOT work on Vercel or Cloudflare serverless. β”‚"
  echo "  β”‚                                                         β”‚"
  echo "  β”‚ You need a cloud database for deployment.               β”‚"
  echo "  β”‚ Free options:                                           β”‚"
  echo "  β”‚   β€’ Supabase  β€” supabase.com/dashboard                 β”‚"
  echo "  β”‚   β€’ MongoDB Atlas β€” mongodb.com/cloud/atlas             β”‚"
  echo "  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜"
  echo ""

  if $INTERACTIVE; then
    echo "  1) Switch to Supabase (enter credentials now)"
    echo "  2) Switch to MongoDB Atlas (enter credentials now)"
    echo "  3) Skip deploy β€” I'll set up a cloud DB later"
    read -rp "Choice [1]: " sqlite_fix
    sqlite_fix="${sqlite_fix:-1}"

    if [ "$sqlite_fix" = "1" ]; then
      DB_PROVIDER="supabase"
      read -rp "Supabase project URL: " SUPABASE_URL
      read -rsp "Supabase service role key: " SUPABASE_KEY
      echo ""

      if [ -z "$SUPABASE_URL" ] || [ -z "$SUPABASE_KEY" ]; then
        echo "❌ Both URL and key are required. Skipping deploy."
        DEPLOY_TARGET="none"
      else
        sed -i "s/^DATABASE_PROVIDER=.*/DATABASE_PROVIDER=supabase/" "$ENV_FILE"
        if ! grep -q "SUPABASE_URL" "$ENV_FILE" 2>/dev/null; then
          echo "" >> "$ENV_FILE"
          echo "# Supabase (added for cloud deploy)" >> "$ENV_FILE"
          echo "SUPABASE_URL=$SUPABASE_URL" >> "$ENV_FILE"
          echo "SUPABASE_SERVICE_KEY=$SUPABASE_KEY" >> "$ENV_FILE"
        else
          sed -i "s|^SUPABASE_URL=.*|SUPABASE_URL=$SUPABASE_URL|" "$ENV_FILE"
          sed -i "s|^SUPABASE_SERVICE_KEY=.*|SUPABASE_SERVICE_KEY=$SUPABASE_KEY|" "$ENV_FILE"
        fi
        echo "βœ… Switched to Supabase. .env.local updated."
        echo ""
        echo "πŸ”¨ Rebuilding..."
        cd "$PLATFORM_DIR"
        npx next build > /dev/null 2>&1 && echo "βœ… Build passed" || echo "⚠️  Build had issues"
      fi

    elif [ "$sqlite_fix" = "2" ]; then
      DB_PROVIDER="mongodb"
      echo ""
      echo "Paste your MongoDB Atlas connection string:"
      echo "  (e.g. mongodb+srv://user:[email protected])"
      read -rp "MongoDB Atlas URI: " MONGODB_URI
      read -rp "Database name [blog]: " MONGODB_DB_NAME
      MONGODB_DB_NAME="${MONGODB_DB_NAME:-blog}"

      if [ -z "$MONGODB_URI" ]; then
        echo "❌ Atlas URI is required. Skipping deploy."
        DEPLOY_TARGET="none"
      else
        sed -i "s/^DATABASE_PROVIDER=.*/DATABASE_PROVIDER=mongodb/" "$ENV_FILE"
        if ! grep -q "MONGODB_URI" "$ENV_FILE" 2>/dev/null; then
          echo "" >> "$ENV_FILE"
          echo "# MongoDB Atlas (added for cloud deploy)" >> "$ENV_FILE"
          echo "MONGODB_URI=$MONGODB_URI" >> "$ENV_FILE"
          echo "MONGODB_DB_NAME=$MONGODB_DB_NAME" >> "$ENV_FILE"
        else
          sed -i "s|^MONGODB_URI=.*|MONGODB_URI=$MONGODB_URI|" "$ENV_FILE"
          sed -i "s|^MONGODB_DB_NAME=.*|MONGODB_DB_NAME=$MONGODB_DB_NAME|" "$ENV_FILE"
        fi
        echo "βœ… Switched to MongoDB Atlas. .env.local updated."
        echo ""
        echo "πŸ”¨ Rebuilding..."
        cd "$PLATFORM_DIR"
        npx next build > /dev/null 2>&1 && echo "βœ… Build passed" || echo "⚠️  Build had issues"
      fi
    else
      echo "⏭️  Skipping deploy. Set up a cloud DB and redeploy later:"
      echo "   1. Create a free Supabase or MongoDB Atlas account"
      echo "   2. Update .env.local with the credentials"
      echo "   3. Change DATABASE_PROVIDER to supabase or mongodb"
      echo "   4. Run: cd platform && npx vercel --prod"
      DEPLOY_TARGET="none"
    fi
  else
    echo "❌ Cannot deploy with SQLite to serverless. Set SETUP_DB_PROVIDER=supabase or mongodb"
    DEPLOY_TARGET="none"
  fi
fi

# ── Vercel Deploy ── 
if [ "$DEPLOY_TARGET" = "vercel" ]; then
  echo ""
  echo "πŸš€ Deploying to Vercel..."
  echo ""

  # Step 1: Install Vercel CLI if missing
  if ! npx -y vercel --version &>/dev/null; then
    echo "πŸ“¦ Installing Vercel CLI..."
    npm install -g vercel
  fi
  echo "   Vercel CLI: $(npx -y vercel --version 2>/dev/null)"
  echo ""

  cd "$PLATFORM_DIR"

  if $INTERACTIVE; then
    # Step 2: Check if logged in, if not β€” login first
    echo "── Step 1: Vercel Login ──"
    echo "Checking authentication..."
    if ! npx -y vercel whoami &>/dev/null 2>&1; then
      echo ""
      echo "You need to log in to Vercel first."
      echo "Options:"
      echo "  1) Log in via browser (opens vercel.com)"
      echo "  2) Log in with email"
      echo "  3) Log in with GitHub"
      read -rp "Choice [1]: " login_method
      login_method="${login_method:-1}"
      case "$login_method" in
        2) npx -y vercel login --email ;;
        3) npx -y vercel login --github ;;
        *) npx -y vercel login ;;
      esac

      # Verify login succeeded
      if ! npx -y vercel whoami &>/dev/null 2>&1; then
        echo "❌ Login failed. You can deploy later with:"
        echo "   cd platform && npx vercel login && npx vercel --prod"
        DEPLOY_TARGET="none"
      else
        echo "βœ… Logged in as: $(npx -y vercel whoami 2>/dev/null)"
      fi
    else
      echo "βœ… Already logged in as: $(npx -y vercel whoami 2>/dev/null)"
    fi

    # Step 3: Link project and deploy
    if [ "$DEPLOY_TARGET" = "vercel" ]; then
      echo ""
      echo "── Step 2: Project Setup & Deploy ──"
      echo ""
      read -rp "Deploy to production? (Y/n): " prod_deploy
      prod_deploy="${prod_deploy:-y}"

      if [[ "$prod_deploy" =~ ^[Yy] ]]; then
        echo ""
        echo "Deploying to production..."
        if npx -y vercel --prod; then
          echo ""
          echo "βœ… Deployed to Vercel (production)!"
        else
          echo ""
          echo "⚠️  Deploy failed. You can retry with:"
          echo "   cd platform && npx vercel --prod"
        fi
      else
        echo ""
        echo "Deploying preview..."
        if npx -y vercel; then
          echo ""
          echo "βœ… Preview deployed to Vercel!"
        else
          echo ""
          echo "⚠️  Deploy failed. You can retry with:"
          echo "   cd platform && npx vercel"
        fi
      fi
    fi

  else
    # Non-interactive β€” requires VERCEL_TOKEN
    if [ -n "${VERCEL_TOKEN:-}" ]; then
      echo "Deploying with token (non-interactive)..."
      if npx -y vercel --token "$VERCEL_TOKEN" --yes --prod 2>&1; then
        echo "βœ… Deployed to Vercel (production)"
      else
        echo "❌ Deploy failed. Check VERCEL_TOKEN and project settings."
      fi
    else
      echo "⚠️  VERCEL_TOKEN not set β€” skipping automated deploy"
      echo "   Set VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_PROJECT_ID env vars"
      echo "   Then run: cd platform && npx vercel --token \$VERCEL_TOKEN --yes --prod"
      DEPLOY_TARGET="none"
    fi
  fi

# ── Cloudflare Deploy ──
elif [ "$DEPLOY_TARGET" = "cloudflare" ]; then
  echo ""
  echo "πŸš€ Deploying to Cloudflare Pages..."
  echo ""

  # Step 1: Install Wrangler CLI if missing
  if ! npx -y wrangler --version &>/dev/null; then
    echo "πŸ“¦ Installing Wrangler CLI..."
    npm install -g wrangler
  fi
  echo "   Wrangler CLI: $(npx -y wrangler --version 2>/dev/null)"
  echo ""

  cd "$PLATFORM_DIR"

  if $INTERACTIVE; then
    # Step 2: Check if logged in
    echo "── Step 1: Cloudflare Login ──"
    echo "Checking authentication..."
    if ! npx -y wrangler whoami &>/dev/null 2>&1; then
      echo ""
      echo "You need to log in to Cloudflare first."
      echo "This will open your browser for OAuth login."
      read -rp "Press Enter to continue (or Ctrl+C to skip)..." _
      npx -y wrangler login

      if ! npx -y wrangler whoami &>/dev/null 2>&1; then
        echo "❌ Login failed. You can deploy later with:"
        echo "   cd platform && npx wrangler login && npx wrangler pages deploy"
        DEPLOY_TARGET="none"
      else
        echo "βœ… Logged in to Cloudflare"
      fi
    else
      echo "βœ… Already logged in to Cloudflare"
    fi

    # Step 3: Deploy
    if [ "$DEPLOY_TARGET" = "cloudflare" ]; then
      echo ""
      echo "── Step 2: Build & Deploy ──"

      # Sanitize project name (lowercase, hyphens only)
      CF_PROJECT=$(echo "${BLOG_NAME}" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-')
      echo "Project name: $CF_PROJECT"
      read -rp "Change project name? (press Enter to keep): " cf_name
      [ -n "$cf_name" ] && CF_PROJECT="$cf_name"

      echo ""
      echo "Building for Cloudflare Pages..."
      if npx -y @cloudflare/next-on-pages 2>&1; then
        echo ""
        echo "Deploying to Cloudflare Pages..."
        if npx -y wrangler pages deploy .vercel/output/static --project-name "$CF_PROJECT" 2>&1; then
          echo ""
          echo "βœ… Deployed to Cloudflare Pages!"
          echo "   Project: $CF_PROJECT"
        else
          echo ""
          echo "⚠️  Deploy failed. You can retry with:"
          echo "   cd platform && npx wrangler pages deploy .vercel/output/static --project-name \"$CF_PROJECT\""
        fi
      else
        echo "❌ Build for Cloudflare failed."
        echo "   Try: cd platform && npx @cloudflare/next-on-pages"
      fi
    fi

  else
    # Non-interactive β€” requires CLOUDFLARE_API_TOKEN
    if [ -n "${CLOUDFLARE_API_TOKEN:-}" ]; then
      CF_PROJECT=$(echo "${BLOG_NAME}" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -cd 'a-z0-9-')
      echo "Building for Cloudflare Pages (project: $CF_PROJECT)..."
      if CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN" npx -y @cloudflare/next-on-pages 2>&1; then
        if CLOUDFLARE_API_TOKEN="$CLOUDFLARE_API_TOKEN" npx -y wrangler pages deploy \
            .vercel/output/static --project-name "$CF_PROJECT" --commit-dirty=true 2>&1; then
          echo "βœ… Deployed to Cloudflare Pages"
        else
          echo "❌ Deploy failed. Check CLOUDFLARE_API_TOKEN."
        fi
      else
        echo "❌ Build for Cloudflare failed."
      fi
    else
      echo "⚠️  CLOUDFLARE_API_TOKEN not set β€” skipping automated deploy"
      echo "   Set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID env vars"
      echo "   Then run: cd platform && npx @cloudflare/next-on-pages && npx wrangler pages deploy .vercel/output/static"
      DEPLOY_TARGET="none"
    fi
  fi

else
  echo "⏭️  Skipping deployment (run locally with 'cd platform && npm run dev')"
fi
echo ""

# ═══════════════════════════════════════
# 7. Summary
# ═══════════════════════════════════════
echo "  ╔══════════════════════════════════════════════╗"
echo "  β•‘   βœ… Setup Complete!                         β•‘"
echo "  ╠══════════════════════════════════════════════╣"
echo "  β•‘                                              β•‘"
printf "  β•‘   Blog:    %-33sβ•‘\n" "$BLOG_NAME"
printf "  β•‘   Theme:   %-33sβ•‘\n" "$THEME"
printf "  β•‘   DB:      %-33sβ•‘\n" "$DB_PROVIDER"
printf "  β•‘   Cache:   %-33sβ•‘\n" "$CACHE_PROVIDER"
printf "  β•‘   Deploy:  %-33sβ•‘\n" "$DEPLOY_TARGET"
echo "  β•‘                                              β•‘"
printf "  β•‘   API Key: %-33sβ•‘\n" "${API_KEY:0:12}..."
echo "  β•‘   ⚠️  Save this key for API calls!           β•‘"
echo "  β•‘                                              β•‘"
echo "  β•‘   Start:  cd platform && npm run dev         β•‘"
echo "  β•‘   Visit:  http://localhost:3000               β•‘"
echo "  β•‘                                              β•‘"
echo "  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"
echo ""

# ── Output config as JSON for agents to parse ──
if ! $INTERACTIVE; then
  echo "SETUP_RESULT_JSON={\"apiKey\":\"$API_KEY\",\"dbProvider\":\"$DB_PROVIDER\",\"cacheProvider\":\"$CACHE_PROVIDER\",\"theme\":\"$THEME\",\"blogName\":\"$BLOG_NAME\",\"deployTarget\":\"$DEPLOY_TARGET\"}"
fi

```

write-my-blog | SkillHub