What do I do when …
shipped 1.0.0The rest of the manual is filed by command. This page is filed by situation,
the form the question arrives in. Not “what does cwp pull do” but “I have
been working in the builder, what now”.
Read the headings for the overview. Read one block to act. Each block says what the situation is, what to type, and what goes wrong. Where cwp does not cover a case, the block says so instead of implying it does.
“I have been working in the page builder. How does that get into the repository?”
Every click in a page builder writes to the database, not to a file. Nothing
in your working copy changed, git status stays empty, and the work exists in
exactly one place. This tool exists to end that state.
cwp status --drift # both directions, no writes
cwp pull # everything under management, into the tree
git diff # this is the review
The pull is not the review. The diff is. The committed file is environment-neutral and canonical, so what you read is the change you made and nothing else. Commits give a builder page a history.
Design-system work comes down in the same run: global classes, theme styles, colour palettes, custom fonts. It is part of what the project manages. Narrow it only when you know what you touched:
cwp pull --only bricks
The reverse direction is the same command backwards. What you changed in the
tree reaches a site with cwp push: pages, the design
system, settings, roles, widget areas and the inventory. Menus and terms travel
alongside the pages that name them.
When both sides moved, cwp stops. _cwp_sync records what cwp last left on
the site. That tells “the file moved” apart from “somebody edited the page
there” rather than guessing. cwp refuses a push over the second until you say
--overwrite-conflicts. cwp status --drift closes
with the two answers and what each one discards. The mistake it exists to
prevent is reaching for whichever direction sounds safer.
“I need the live content on my machine”
Which command depends on what you mean by the content.
cwp pull brings the environment’s tree down into
files you can read, diff and commit: posts, terms, menus, settings, roles,
widget areas, the design system. It imports nothing into your database, and
nothing about your local site changes except the tree.
cwp db pull brings the database itself down and
makes it safe to hold. A local snapshot first, then the import, then the URL
rewrite, then the scrub. This is the one that replaces what
you have locally.
cwp pull dev # the tree, downward — nothing imported
cwp db pull dev # the database, scrubbed, snapshot first
cwp media pull prod --uploads sync # the whole media library
Two things happen whether you ask for them or not. cwp neutralises outbound
mail on every pull, even with scrubbing switched off: diverted to Mailpit
when it answers, blocked otherwise. And cwp removes personal data. It
anonymises subscribers and customers. It keeps the staff roles you named so
you can still log in. It empties the logs you list under pull.truncate_tables,
truncate_post_types and truncate_taxonomies. The defaults cover a Bricks
and SNN-BRX stack, where plugins store visitor IP addresses and user agents as
taxonomy terms.
What goes wrong: cwp db pull replaces the local database wholesale. A
page you built locally this morning and have not pulled into the tree is gone,
and unlike files, it is not in git. Run cwp status --drift first. cwp pull
what you built into the tree. Remember that the pre-pull snapshot exists:
cwp db list
cwp db restore pre-pull-dev-20260813-113000
“Our tags have grown wild. Can I clean them up locally?”
Yes, when the group declares the taxonomies. Terms then travel as files with a uid identity of their own. A rename is then a rename instead of a delete plus a create:
content:
post_types: [page, post]
taxonomies: [category, post_tag]
cwp pull dev # posts, terms and menus among the rest
# merge locally: retag the posts in the tree,
# and delete the term files you dropped
cwp push dev --only content --delete-terms
cwp creates the terms the tree describes, in parent-before-child order. It
refuses to remove one that posts still carry. The count is WordPress’s own and
covers every post on the site, not only the managed ones. --delete-terms
compares the whole tree, and a crossing carries the whole tree by definition.
On cwp push it needs no companion flag; on the older cwp content push it
still needs --all.
The limits, before you find them: a taxonomy nobody declared does not travel
at all. That is deliberate. Some plugins store visitor IP addresses as terms,
and a feature that discovered taxonomies would commit them to a git
repository. A term slug a post references with no term file behind it stays
missing: the post arrives without it and the run says so. And declaring a
taxonomy that pull.truncate_taxonomies also empties is a contradiction
cwp doctor catches for you.
Navigation menus work the same way: declare menus: on one group, and each menu
becomes a file whose entries point at pages by identity rather than by post id.
They travel with --all only.
“WordPress core is behind”
cwp’s role here is smaller than you might expect, on purpose. The platform belongs to the host. A managed host applies its own updates, and cwp reaching up to change WordPress itself would be the most invasive write it offers.
What it does do:
cwp doctor # local 6.8.2 vs dev 6.9, reported
cwp pull dev --only inventory # record its version and locale
cwp push --only inventory # move the *local* site forward
The recorded version is a floor, never a target. A site ahead of the file
is not drift and nothing happens. apply never brings one back down to a
number a stale file happens to hold. The database schema update runs with it:
a site whose files moved without its database is worse off than one that did
not update at all.
What goes wrong: nothing here updates a remote. If the environment is the one that is behind, that is a conversation with your host, or an update in its own admin. It is not a flag on a cwp command.
“This machine has none of the plugins”
inventory.yml is the committed record of what a working copy needs: WordPress
at a version, in a locale, with these plugins and these themes.
cwp pull writes it and cwp push
applies it, both under --only inventory.
cwp pull dev --only inventory # write the file from that site
cwp fetch dev # bring the code itself down
cwp push --only inventory # install, activate, update
cwp push --only inventory --prune # …and delete what it omits
Your own code is deliberately absent from that file. A site plugin, a child
theme, a block of custom elements: those live under tracked: in cwp.yml.
They are in git, and cwp deploys them rather than installing them. Installing
them from a registry is not a thing that could work.
cwp also reads the older name plugins.yml and writes the current one back, and
cwp plugins stays as an alias.
What goes wrong: --prune deletes. That flag makes an environment match the
file exactly, and there is no undo beyond the restore point cwp takes first.
Run it once with --dry-run.
“The site is German. What about translations?”
The inventory records the active locale and every installed translation, and
cwp push --only inventory installs the ones a local site is missing. It
installs rather than switches. Which language a site runs in is a setting
somebody chose inside that site, and cwp makes sure the translation is present
without touching the choice.
Beyond that, this is an open area rather than a feature, and the honest list is short:
- cwp handles translations locally only, exactly as it handles the core update. It does not install a language on a remote.
wp-content/languages/is not special to cwp. It travels only if you list it undertracked:, and then it travels as any other directory of files.cwp doctorcompares the WordPress version between local and an environment. It does not compare locales, so it does not report a remote missing a translation your local has.
If your project depends on translation files moving between environments, track the directory and treat it as code. Do not assume cwp is watching it.
“I want my local state on an environment”
One command, and it carries everything under management. It is not the bulk upward write this tool refuses to have. Every item in it has a reviewed, committed source, and the plan lists it before the write.
| What moves | Where it comes from |
|---|---|
| Your code | the tracked: paths |
| Pages, posts, terms and menus | content/, as database rows listed before the write |
| The design system | bricks/: global classes, theme styles, palettes, fonts |
| Settings, widget areas, roles, the inventory | their own files |
cwp push dev --dry-run # the whole plan, nothing written
cwp deploy dev # …and then do it, with the post-steps
cwp push dev --only content # one artifact, when you know what you touched
Use deploy rather than cwp push unless you have a
reason. push moves everything and leaves the site holding a stale cache and
stale generated CSS. That looks exactly like a deploy that did not work.
What is not here is the database. There is no upward database command, and
cwp deploy is not one.
Two guards you will meet, and they answer different questions. An environment
marked mode: protected refuses every upward write without --force. That flag
says “yes, I mean production”. A post that changed on the target since cwp last
saw it refuses without --overwrite-conflicts. That flag says “yes, discard
what somebody changed there”. Answering the second by accident while answering
the first is the reason they are two flags.
Before any upward write cwp takes a restore point on the far side. On a host that
cannot take one, --no-backup makes cwp refuse the write rather than
quietly leaving it unprotected. And --dry-run still runs the refusals: a plan
for a write cwp would never permit is a description of a different command.
What cwp deliberately does not do
- There is no bulk database push. An upward write whose extent nobody can state beforehand is the failure this tool takes its name from. Individually named items go up instead: pages, terms, menus, design-system parts, listed before the write and under the full guard set.
- It does not merge two versions of a page. It tells you both moved and stops.
- A deletion does not travel downward. A pull names the tree items the source
no longer has and removes nothing: deleting out of a git-tracked directory
belongs in a commit, not in a side effect of a read. Upward, deletion is opt-in
and needs the whole tree in scope (
--delete,--delete-terms). - It does not manage the media library.
cwp pushuploads the attachments a pushed page references; nothing else about uploads goes up, ever. - It does not update WordPress, plugins or translations on a remote as a side effect. Every one of those is a named command you type at an environment.
- It does not discover. You declare post types, taxonomies and menus in
cwp.yml. A tool that scanned for them would one day carry something personal into a public repository. - It does not clean up your themes directory. Custom code that accumulated in a child theme or a snippet manager is yours to move. cwp cannot tell your code from the theme author’s, and a migration that guessed would be worse than none.
Where the detail is
This page answers “what do I do”. The reference answers “what does this flag do”: every command, the configuration file, the safety model and the scrub pipeline.