Custom Post Types
The cpt command group declares WordPress custom post types in code, like CPT UI but from files in your repository instead of an admin screen. Each post type is one JSON file holding the arguments of WordPress’s own register_post_type(), and the Loopress plugin registers it on every request.
A cpt/ file is data, never PHP: Loopress refuses the few arguments WordPress would run as code (see File format). That’s why custom post types work with both editions of the plugin, Loopress Light included.
Typical workflow
Section titled “Typical workflow”# 1. Declare a post typemkdir -p cpt && cat > cpt/book.json <<'EOF'{ "labels": { "name": "Books", "singular_name": "Book" }, "public": true, "has_archive": true, "show_in_rest": true, "menu_icon": "dashicons-book", "supports": ["title", "editor", "thumbnail"], "rewrite": { "slug": "books" }}EOF
# 2. Commit itgit add cpt/ && git commit -m "feat: add a book post type"
# 3. Deploy it to WordPresslps cpt push“Books” now appears in the wp-admin menu. Permalinks are refreshed once, on the request after the push, so /books/ works without visiting Settings > Permalinks.
Commands
Section titled “Commands”Every command below also accepts the common flags: --env, --json, and --yes where it applies.
lps cpt push
Section titled “lps cpt push”Upload local cpt/*.json files to WordPress.
lps cpt push [path]Each file creates the post type or replaces its arguments. The push never deletes a post type: to stop registering one, use lps cpt rm. If the post type was changed on WordPress since it was last read, the push for it is refused instead of overwriting that change.
| Argument | Default | Description |
|---|---|---|
path |
./cpt (or loopress.json’s cptDir) |
Local directory to read .json files from |
| Flag | Description |
|---|---|
--dry-run / -d |
Show what would be pushed without making any changes |
lps cpt pull
Section titled “lps cpt pull”Download the post types Loopress manages on WordPress into cpt/<slug>.json files.
lps cpt pull [path]Only Loopress post types are pulled. Post types registered by WordPress, a theme or another plugin (ACF, CPT UI…) are never written to cpt/, see lps cpt list for those.
| Argument | Default | Description |
|---|---|---|
path |
./cpt (or loopress.json’s cptDir) |
Local directory where files are written |
| Flag | Description |
|---|---|
--dry-run / -d |
Show what would be written without touching the filesystem |
Local files no longer present on WordPress are removed on pull, see pull mirrors the site.
lps cpt list
Section titled “lps cpt list”Print every post type registered on WordPress and where it comes from: Loopress, WordPress, ACF, CPT UI, or a theme or plugin.
lps cpt listExample output:
post (Posts): WordPress, 12 publishedpage (Pages): WordPress, 5 publishedbook (Books): Loopress, 3 publishedevent (Events): ACF, 8 publishedA post type declared in cpt/ whose slug was already registered by something else is skipped, never overwritten, and list warns about it.
lps cpt rm
Section titled “lps cpt rm”Stop registering a Loopress post type.
lps cpt rm <slug>Its posts are not deleted: they stay in the database, hidden from wp-admin and the site until a post type with the same slug is registered again. The local file is left alone, delete it yourself or the next lps cpt push registers the post type again. Asks for confirmation first, --yes skips it.
lps cpt diff
Section titled “lps cpt diff”Show what differs between your local cpt/ files and a WordPress environment, or between two environments with --against. Also part of the aggregate lps diff (--only cpt).
lps cpt diff [path]lps cpt diff --env staging --against productionExits 0 in sync, 1 on drift, 2 when the comparison failed.
lps cpt rollback
Section titled “lps cpt rollback”Restore post types on WordPress to the snapshot saved automatically before an earlier lps cpt push. See Rollback and Snapshots.
lps cpt rollback # the most recent snapshotlps cpt rollback --list # available snapshotslps cpt rollback --to <id> # an older oneFile format
Section titled “File format”cpt/ book.json event.jsonThe file name is the post type’s slug (its permanent key in the database): 1 to 20 lowercase letters, digits, _ or -. WordPress’s own post types (post, page, attachment…) and any wp_ slug are reserved.
The content is the $args array of register_post_type(), as JSON. Every argument documented by WordPress works, with these exceptions, refused because WordPress would run them as code:
register_meta_box_cbrest_controller_class,autosave_rest_controller_class,revisions_rest_controller_class
Need one of those? Register that post type from a hook instead.
Arguments WordPress uses as arrays must be arrays: capabilities, labels, taxonomies and template always, supports or false, rewrite or true/false. A wrong type is refused at push, it would otherwise break every page of the site. lps validate checks the file name and all of the above before you push.
In wp-admin
Section titled “In wp-admin”The Config tab of the Loopress admin page lists every post type on the site, read-only: its name, slug, source (Loopress, WordPress, ACF, CPT UI, or a theme or plugin) and number of published posts. A Loopress post type skipped because its slug was already registered shows a Conflict badge.
Other ways to declare post types
Section titled “Other ways to declare post types”| Where the post type lives | How Loopress syncs it |
|---|---|
cpt/<slug>.json files |
lps cpt, this page |
| ACF’s own post type screen (ACF 6.1+) | lps acf, --type post-types |
PHP code calling register_post_type() |
a hook (Loopress Full) |
Pick one per post type: two sources registering the same slug is exactly the conflict the admin page flags.