Skip to content

cwp status

shipped 1.0.0
cwp status [env] [flags]

Acts on the environment the project makes obvious: the only one it defines, or the one called dev. Name another to act there. A project with several and no dev has to name one.

ArgumentWhat it isDefault
[env]environment for the remote column
FlagWhat it doesDefault
--driftcompare the local site against the committed treeoff
--remotecompare an environment as well (implies —drift)off
--fail-on-driftexit 1 when anything differs (implies —drift)off
--with-agentwith —dry-run on a host with no shell: install and remove the PHP agent so the plan is realoff

Plus the shared flags --json, -v, --verbose, -q, --quiet and --dry-run.

What it does

The “where am I in this project” screen. It saves you ddev describe, cat cwp.yml and cwp db list run one after another.

$ cwp status
✓ cwp — 1.0.0 (32598ac-dirty, built 2026-08-29 09:01)
✓ project — example.com — ~/Code/example/example.com
✓ docroot — public
✓ php — 8.3
✓ environment local — https://example.com.ddev.site — this working copy — not adopted
✓ environment dev — https://example.com — app example.com — managed — not adopted
– ddev — unhealthy — https://example.com.ddev.site
– last pull with a snapshot (local) — unknown — no pre-pull snapshot on disk. A `cwp db pull --no-snapshot`, or a `cwp media pull`, leaves none, and `cwp db rm` removes the record with the restore point
– last pull with a snapshot (dev) — unknown — no pre-pull snapshot on disk. A `cwp db pull --no-snapshot`, or a `cwp media pull`, leaves none, and `cwp db rm` removes the record with the restore point
– snapshots — none in ~/Code/example/example.com/.ddev/db_snapshots
  artifact       repo  detail
! bricks         ?     not a git repository, or git did not answer
! content/       ?     not a git repository, or git did not answer
– inventory.yml  –     no inventory.yml. Run `cwp pull --only inventory`
– settings/      –     no `settings:` block. This project does not carry options
– roles.yml      –     no roles.yml. Run `cwp pull --only roles`
– widgets/       –     no `widgets:` declaration. This project does not carry widget areas
  only the repo column was read. Pass --drift to compare the local site, --remote to compare an environment as well

Captured from cwp 1.0.0. Not written by hand.

It makes no remote calls unless you ask for them. It reads the config, the DDEV state, the snapshot directory and git status, and nothing else. It has to be instant and has to work on a train, with Docker stopped and no login anywhere. cwp doctor talks to the remote and takes as long as that costs.

The first line says which cwp answered

✓ cwp — 1.0.0 (49fa29e, built 2026-08-17 19:17)

Everything under it is that binary’s account of the project. The release number alone cannot say which binary that was: it moves at a release and not once in the commits between two, so two builds a fortnight apart report the same 1.0.0.

The line matters on a working copy installed with npm link, where the bundle and the source beside it are different things. When the source is newer the line warns and says so. -dirty means the build came from a tree with uncommitted changes and matches no commit. A published install prints its version and nothing more: there is no second identity there to be wrong about.

Drift: is what you have what you committed?

The last lines are one per tracked artifact. By default they answer one question: whether you committed that tree.

  artifact       repo  detail
✓ bricks         ✓
✓ content/       ✓
✓ inventory.yml  ✓
  only the repo column was read — pass --drift to compare the local site,
  --remote to compare an environment as well

--drift adds the local column: whether the site on this machine holds what the tree says. --remote adds a third for an environment, and implies the first.

  artifact       repo  local  prod  detail
✓ bricks         ✓     ✓      ✓
! content/       ✓     ✗      ✓     2 of 8 page(s) differ on local
! inventory.yml  ✓     ✓      ✗     2 difference(s) on prod

Under the project lines, cwp names every line in cwp.yml whose reason no longer holds, with the way to renew it (the reason beside a line).

The environment lines carry the stage where the project declares one. cwp names an environment without one once, with the line that sets it.

Each column heading names the side it is about, and each side has its own colour. The tree is cyan, the local site magenta, the environment blue. An environment marked protected takes the warn colour instead, because a mistake on that side is expensive. Every command that names a side uses the same colours, so you can find the column you care about without reading the headings. In a pipe, under NO_COLOR or on a monochrome terminal the headings still say it: colour never carries the answer alone.

Both are flags because both cost something. The remote column needs the network and the local column needs a running container. A bare cwp status stays half a second.

A cell reads ✓ agree, ✗ differ, – this project has no such artifact, and ? asked and got no answer. The last one covers Docker stopped, a remote that did not reply, a tree that would not read. ? never rounds to either side, and a run with one of them says so rather than closing with “everything agrees”.

cwp compares each artifact with the machinery that moves it: the same drift calculation cwp content status reports per page, the same diff cwp push --only inventory acts on, the same export cwp pull --only bricks writes. cwp status has no second opinion about what “differs” means.

The inventory row reads both directions, and reports them separately because they are different facts with different fixes:

! inventory.yml  ✓     ✗      2 difference(s) on local — `cwp push --only inventory` would act;
                                1 installed on local and not recorded
                                (bricks-navigator) — `cwp pull --only inventory`

The second half is how a project drifts in practice: somebody installs something, nobody records it, and otherwise the row stays green. cwp push --only inventory does nothing about it, because without --prune it does not look at the installed side. So the line points the other way, at reading the environment down.

The table covers the tree, and says what it does not

Five artifacts is not a site. Options, roles that no roles.yml records, widget areas and every post type no content group declares stay outside the comparison. So a run that found no drift closes with everything the tree describes agrees with it, and names what it did not look at:

  outside the tree entirely, and therefore not compared above:
  post types (28), taxonomies (14), options (181), widget areas (1)
  — `cwp coverage` says what

cwp measures that line rather than writing it: it comes from the same census cwp coverage runs, so it shrinks by itself as a project starts carrying more. It costs a read of the local site, so it appears only where a run reads a side anyway. The bare command still compares nothing, says so, and needs neither Docker nor a network.

Drift warns, never fails. It is a fact about a project rather than an error in it. A command that exited non-zero over an unpushed page would be unusable in the loop it exists to serve.

--fail-on-drift is the exception that says so

A pre-push hook needs one line that can abort, and the default above cannot be it. --fail-on-drift prints what --drift prints, implies it the way --remote does, and exits 1 when any row differs:

cwp status --fail-on-drift

A column it could not read fails too. The failure mode of a gate is passing for the wrong reason: DDEV down, nothing compared, the deploy goes ahead. The ? cells say which side gave no answer. The run says this was not a clean comparison.

cwp init scaffolds the line commented out in cwp/hooks/pre-push.sh. Uncomment it and the gate is on. It guards the build loop.

The last pull is read off the snapshots

cwp keeps no state file for it, and so has nothing to write, gitignore and keep consistent. Every pull leaves pre-pull-<env>-<timestamp>, so the restore point already records when and from which environment. That reading has three limits. The output says “last pull with a snapshot” because of the first:

  1. A pull with --no-snapshot, or a cwp media pull that touches no database, leaves no snapshot and is invisible here.
  2. cwp db rm on a pre-pull-* snapshot deletes the record along with the restore point. Reclaiming disk space quietly rewrites this history.
  3. The snapshot is taken before the import, so it proves a pull got past the confirmation guard, not that it finished. A pull that died on the transfer leaves the same trace as one that succeeded.

So an environment with no pre-pull-* snapshot reads unknown with the reason. It never reads “never pulled”, because cwp cannot know that.

What it does not do

It contacts no remote unless you pass --remote. It starts nothing. It writes nothing to git: the repo column is a git status, and cwp never stages, commits or branches on your behalf.

--dry-run means nothing here, and cwp ignores it, as cwp doctor does.