Skip to content

What do I do when …

shipped 1.0.0

The 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 under tracked:, and then it travels as any other directory of files.
  • cwp doctor compares 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 movesWhere it comes from
Your codethe tracked: paths
Pages, posts, terms and menuscontent/, as database rows listed before the write
The design systembricks/: global classes, theme styles, palettes, fonts
Settings, widget areas, roles, the inventorytheir 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 push uploads 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.