A small Node command-line tool for moving content between disk and a Ghost site via the Ghost Admin API. Built for lifeaccordingtojay.com, generic enough to point at any Ghost instance.
Safe writes by default. Every destructive command supports
--dry-run. Thepushcommand saves a JSON snapshot of the current state tosnapshots/before any write, so nothing is lost.
Seven commands. list, get, push and create all take --type, which accepts post or page and defaults to posts.
npm run ping, verify the connection and print the site title and total post countnpm run list [-- --type post|page|all], list content with title, status, slug and custom templatenpm run tags [-- --add "#mimer,#skald" [--dry-run]], list every tag, or create any named tags that do not already exist. Idempotent, so existing tags are left alonenpm run get -- --slug <slug> [--type <type>] [--out <path>], fetch a body as HTML to a local filenpm run push -- --slug <slug> --file <path> [--type <type>] [--template <name>] [--html-card] [--dry-run], replace a body with HTML from a file, taking a snapshot firstnpm run create -- --file <path> --title <title> --slug <slug> [--type <type>] [--template <name>] [--html-card] [--tags "..."] [--excerpt "..."] [--dry-run], create a new draft from HTMLnpm run check -- --title <fragment> [--title <fragment> ...], read-only inspection. Finds content whose title contains any of the given fragments, case-insensitive substring, and prints slug, status and URL. Repeatable for multiple lookups
All commands read credentials from .env. Nothing is hardcoded.
--type page targets the pages endpoint rather than posts. Ghost enforces slug uniqueness across posts and pages together, so create checks both and refuses if either already holds the slug.
--template <name> sets the custom template, which must be registered under custom in the theme's package.json. Ghost stores and returns the bare filename, custom-project rather than custom-project.hbs. push accepts it too, so a wrong template can be corrected across many pages without recreating any of them.
--html-card is the one worth understanding before a first run. Ghost converts posted HTML into lexical, and lexical only represents the node types it knows about. Prose, headings, lists and quotes survive. Divs, spans, class attributes and inline SVG do not, and are flattened into bare paragraphs. This fails silently and still returns a clean 200, so the first sign of trouble is a rendered page with its structure gone.
The flag wraps the file in kg-card-begin and kg-card-end markers, which tell the converter to keep the markup verbatim in a single HTML card. Inline SVG, inline styles and <script> blocks all survive intact. Use it for authored markup that depends on its own classes. Leave it off for articles, where real lexical paragraphs are what you want.
Requires Node 18 or later.
- Clone the repo
npm install- Copy
.env.templateto.envand fill in the three values, described below npm run pingto verify
On Windows, PowerShell execution policy can block npm.ps1, which makes npm run unusable from some shells. Call the scripts directly instead, with the same arguments and no -- separator, for example node src/push-body.js --type page --slug mimer --file body.html --html-card.
Three environment variables in .env:
GHOST_API_URL, your Ghost admin URL. Use the underlying.ghost.ioURL, not the custom domain. This is the most common setup mistakeGHOST_ADMIN_API_KEY, generated under Ghost admin, Settings, Integrations, Add custom integration. Copy the Admin API Key, not the Content API KeyGHOST_API_VERSION, typicallyv5.0
The push command:
- Looks up the target post or page by slug
- Saves the current state as a timestamped JSON file in
snapshots/ - Writes the new body
- Reports the result
Writes also carry the record's updated_at, so if it was edited in Ghost admin between the read and the write, Ghost rejects the stale write rather than overwriting the edit.
If you push the wrong file, the previous state lives in snapshots/ and can be restored manually. The folder is gitignored.
push, create and tags all support --dry-run, which prints what would happen without writing.
A general-purpose Ghost CLI. It does the handful of things one author needs to maintain one editorial site. No content modeling, no asset uploads, no theme operations, no member management. Images in particular have to be uploaded through the Ghost editor, since there is no media library in Ghost admin and no upload command here. If you need more, see the official Ghost Admin API docs and build on the same @tryghost/admin-api client library this tool uses.
No license. Public for reference, not packaged for redistribution or reuse.
Jakob Jarefjäll · lifeaccordingtojay.com