# GhostOff plan
A TypeScript CLI tool that exports public posts from a Ghost CMS using its Content API, transforms the HTML body and images into Offprint's block-based format, and uploads the resulting records to an AT Protocol PDS as `site.standard.document` + `app.offprint.document.article` pairs.
## Credentials and configuration
Values can be read from a local `.env` file or passed as CLI flags:
- `GHOST_URL` / `--ghost-url` — Ghost publication URL
- `GHOST_API_KEY` / `--ghost-api-key` — Ghost Content API key
- `ATP_IDENTIFIER` / `--atproto-identifier` — atproto handle or DID
- `ATP_APP_PASSWORD` / `--atproto-app-password` — atproto app password
- `ATP_SERVICE` / `--atproto-service` — PDS/service URL (default `https://bsky.social`)
- `ATPUBLICATION_AT_URI` / `--publication-at-uri` — existing `site.standard.publication` AT-URI to attach documents to
Additional CLI flags:
- `--dry-run` — build records without uploading
- `--export-dir` — local download/cache directory (default `ghostoff-export/`)
- `--state-file` — idempotency state (default `ghostoff-state.json`)
## Tech stack
- Node.js + TypeScript
- AT Protocol SDK: `@atcute/client` + `@atcute/password-session`
- AT Protocol schema/validation: `@atcute/atproto`, `@atcute/standard-site`, `@atcute/lexicons`
- DOM parsing: `linkedom`
- Image resize/compress: `sharp`
- CLI/config: `commander`, `dotenv`
## Layout
```
src/
├── index.ts # CLI orchestration
├── config.ts # env + argv loading
├── ghost.ts # Ghost Content API client
├── assets.ts # image download, cache, resize
├── html-to-offprint.ts # Ghost HTML -> app.offprint.content blocks
├── facets.ts # inline formatting -> richtext facets
└── atproto.ts # auth, blob upload, create/put records, validation
```
## Workflow
1. Load configuration from `.env` and/or CLI flags.
2. Authenticate with the PDS via `@atcute/password-session` and persist the session to `ghostoff-session.json`.
3. Fetch all public Ghost posts with `?include=authors,tags&formats=html,plaintext`, paginated.
4. For each post:
- Download the `feature_image` and any inline `` assets into `--export-dir`.
- Resize/compress images that approach the 1 MB atproto blob limit using `sharp`.
- Convert the post HTML into an `app.offprint.content` wrapper inside the document's `content` array.
- Map top-level block elements:
- `