wp-cli
Terminal-first WordPress operations. Safer than wp-admin for bulk work, scriptable, SSH-friendly, and won't time out on 10k-row updates.
Only inspection commands and wp db export (which takes a backup) run without a prompt. Every mutating command — search-replace, db import, db reset, plugin/theme installs, user and post writes — prompts before it runs. That friction is deliberate: this skill routinely points at production.
Reference files live in ${CLAUDE_SKILL_DIR}/references/.
When NOT to use
- One-off edits a content editor would do in 10 seconds in the admin UI. CLI is overkill and error-prone for single-row clicks.
- Data transformations that span millions of rows — WP's ORM will be slow; consider a direct SQL migration with an explicit transaction.
- Anything that needs to run inside a WordPress plugin's hook lifecycle (e.g., REST validation, custom post type registration) — that's PHP code, not CLI.
- Destructive commands without a current backup.
wp db reset, wp db clean, bulk deletes — always snapshot first.
Preflight
Always verify before doing anything:
wp --version # wp-cli is installed
wp core is-installed # WordPress is actually set up at this path
wp core version # which WP version
If not at the WordPress root, pass --path=/path/to/wordpress or cd there first. On remote hosts, use --ssh=user@host/path or a configured alias (see Remote Execution).
Safety patterns
Backup before destructive work
# Database snapshot
wp db export backup-$(date +%Y%m%d-%H%M%S).sql
# Full site snapshot (db + content)
tar -czf site-backup-$(date +%Y%m%d-%H%M%S).tar.gz wp-content/ backup-*.sql
Dry-run when available
wp plugin update --all --dry-run
wp search-replace 'old.com' 'new.com' --dry-run
Dangerous commands — confirm explicitly
These are irreversible without a backup. Confirm with the user before running:
wp db reset — drops every table
wp db clean — drops every table matching the current $table_prefix
wp site delete (multisite) — removes a site and its content
wp user delete without --reassign — orphans the user's posts (they get deleted too). Always pass --reassign=<new_author_id> to reassign their content first. The flag exists because orphaning content is almost never what you want — the user's posts are the institutional record, not the user row.
wp post delete $(wp post list --format=ids) and similar bulk-delete pipes — quietly turn the whole site into a blank page if the filter is wrong.
Performance flags
--format=json # machine-readable
--format=csv # spreadsheet import
--format=ids # space-separated IDs, for piping
--fields=ID,post_title # return only what you need
--skip-plugins # bypass plugin load (fast, may break plugin-dependent commands)
--skip-themes # bypass theme load
--quiet # suppress info output
--skip-plugins is a scalpel: it makes wp db export instant, but breaks commands that rely on a plugin's hooks (e.g., ACF exporters, custom taxonomies registered by a plugin).
Command catalog
Full flag docs and gotchas are in references/commands.md. Categories:
| Category |
Prefix |
Typical use |
| Database |
wp db |
export, import, query, optimize, search-replace |
| Core |
wp core |
install, update, verify-checksums, version |
| Plugins |
wp plugin |
list, install, activate, update, delete |
| Themes |
wp theme |
list, install, activate, update, delete, theme mod |
| Users |
wp user |
create, list, delete (--reassign), update, roles, generate |
| Posts / Pages |
wp post |
list, create, update, delete, generate, post term |
| Comments |
wp comment |
list, approve, spam, trash, delete |
| Options |
wp option |
get, update, delete, list, pluck/patch for nested values |
| Cache |
wp cache / wp transient |
flush, delete, set, list |
| Cron |
wp cron |
event list/run/schedule/delete, cron test |
| Config |
wp config |
create, get, set, shuffle-salts |
| Multisite |
wp site |
list, create, delete, empty, activate/archive |
Workflows
Full step-by-step workflows live in references/, split by task — load only the one the user is doing end-to-end:
| Workflow |
File |
| Site migration (local → prod) + search-replace |
references/examples-migration.md |
| Plugin updates with rollback, debugging, DB maintenance |
references/examples-maintenance.md |
| User audit, cleanup, safe password rotation |
references/examples-users.md |
| Bulk content import from CSV |
references/examples-content.md |
| Multisite / network operations |
references/examples-multisite.md |
Remote execution
Ad-hoc SSH
ssh user@example.com "cd /var/www/html && wp plugin list"
Configured aliases (preferred)
Define aliases once in ~/.wp-cli/config.yml:
@prod:
ssh: user@example.com/var/www/html
@staging:
ssh: user@staging.example.com/var/www/staging
Then:
wp @prod plugin list
wp @staging db export
Aliases beat ad-hoc SSH because they compose with every wp-cli flag (wp @prod --dry-run search-replace ...) and don't invite shell-quoting bugs.
Common errors
| Error |
Cause |
Fix |
The site you have requested is not installed |
Not at WordPress root / wp-config.php missing |
cd to root or --path=/path/to/wordpress |
MySQL connection failed |
Bad credentials or MySQL down |
Check wp-config.php, confirm MySQL is running |
Error: Can't select database |
DB doesn't exist |
wp db create |
PHP Fatal error: Allowed memory size exhausted |
Large op or heavy plugins |
php -d memory_limit=512M $(which wp) db export |
This does not seem to be a WordPress installation |
WP files not found |
Check directory; confirm WP is actually installed |
The \guid` column is often used ... but should not be updated` |
Expected warning |
--skip-columns=guid suppresses it; GUIDs are permanent IDs, not URLs |
Antipatterns — when wp-cli is the wrong tool
- Looping over millions of rows in shell with
xargs or for. Each wp invocation bootstraps WordPress; 1M iterations = days. Prefer a single wp db query with a proper SQL statement, or a PHP-side batched job.
- Using
wp search-replace without --precise on serialized data larger than a hobby blog. --precise is slower but handles PHP serialized strings correctly; without it, serialized arrays silently corrupt.
- Validation and domain logic. If the logic belongs inside a WP plugin's hook lifecycle, do it there. wp-cli is for operational work, not business rules.
- Credential-bearing commands in your shell history.
wp user create with --user_pass=... leaks. Pipe the password in or let wp-cli prompt.
Best practices
- Staging first, always. Never run an untested command on production.
- Track
wp-config.php changes in version control when feasible (exclude secrets).
- Read the output. wp-cli warnings often precede data loss.
- Document non-obvious workflows — migrations, multisite conversions — as they happen; future-you will not remember which flags you used.
--format=json whenever piping to jq or another script. The default human format rots.
- Verify after big changes — load the homepage, check admin, exercise critical paths.
- Keep wp-cli current:
wp cli update (Phar installs only — Homebrew and Composer installs update through their package manager).
References
references/commands.md — detailed command + flag reference by category
- Workflow guides (load on demand):
references/examples-migration.md — migration + search-replace
references/examples-maintenance.md — plugin updates, debugging, DB maintenance
references/examples-users.md — user audit & cleanup
references/examples-content.md — CSV content import
references/examples-multisite.md — multisite operations
- Upstream docs: https://developer.wordpress.org/cli/commands/
1---2name: wp-cli3description: Drive WordPress from the command line via `wp` CLI — site migrations, search-replace, bulk plugin/theme/user/post operations, option and config edits, multisite management, and cron scheduling. Use whenever the user wants to do something to a WordPress site that a terminal can reach faster than wp-admin.4---56# wp-cli78Terminal-first WordPress operations. Safer than wp-admin for bulk work, scriptable, SSH-friendly, and won't time out on 10k-row updates.910Only inspection commands and `wp db export` (which takes a backup) run without a prompt. Every mutating command — `search-replace`, `db import`, `db reset`, plugin/theme installs, user and post writes — prompts before it runs. That friction is deliberate: this skill routinely points at production.1112Reference files live in `${CLAUDE_SKILL_DIR}/references/`.1314## When NOT to use1516- One-off edits a content editor would do in 10 seconds in the admin UI. CLI is overkill and error-prone for single-row clicks.17- Data transformations that span millions of rows — WP's ORM will be slow; consider a direct SQL migration with an explicit transaction.18- Anything that needs to run inside a WordPress plugin's hook lifecycle (e.g., REST validation, custom post type registration) — that's PHP code, not CLI.19- Destructive commands without a current backup. `wp db reset`, `wp db clean`, bulk deletes — always snapshot first.2021## Preflight2223Always verify before doing anything:2425```bash26wp --version # wp-cli is installed27wp core is-installed # WordPress is actually set up at this path28wp core version # which WP version29```3031If not at the WordPress root, pass `--path=/path/to/wordpress` or `cd` there first. On remote hosts, use `--ssh=user@host/path` or a configured alias (see [Remote Execution](#remote-execution)).3233## Safety patterns3435### Backup before destructive work3637```bash38# Database snapshot39wp db export backup-$(date +%Y%m%d-%H%M%S).sql4041# Full site snapshot (db + content)42tar -czf site-backup-$(date +%Y%m%d-%H%M%S).tar.gz wp-content/ backup-*.sql43```4445### Dry-run when available4647```bash48wp plugin update --all --dry-run49wp search-replace 'old.com' 'new.com' --dry-run50```5152### Dangerous commands — confirm explicitly5354These are irreversible without a backup. Confirm with the user before running:5556- `wp db reset` — drops every table57- `wp db clean` — drops every table matching the current `$table_prefix`58- `wp site delete` (multisite) — removes a site and its content59- `wp user delete` without `--reassign` — orphans the user's posts (they get deleted too). Always pass `--reassign=<new_author_id>` to reassign their content first. The flag exists because orphaning content is almost never what you want — the user's posts are the institutional record, not the user row.60- `wp post delete $(wp post list --format=ids)` and similar bulk-delete pipes — quietly turn the whole site into a blank page if the filter is wrong.6162## Performance flags6364```bash65--format=json # machine-readable66--format=csv # spreadsheet import67--format=ids # space-separated IDs, for piping68--fields=ID,post_title # return only what you need69--skip-plugins # bypass plugin load (fast, may break plugin-dependent commands)70--skip-themes # bypass theme load71--quiet # suppress info output72```7374`--skip-plugins` is a scalpel: it makes `wp db export` instant, but breaks commands that rely on a plugin's hooks (e.g., ACF exporters, custom taxonomies registered by a plugin).7576## Command catalog7778Full flag docs and gotchas are in [`references/commands.md`](references/commands.md). Categories:7980| Category | Prefix | Typical use |81| --- | --- | --- |82| Database | `wp db` | export, import, query, optimize, search-replace |83| Core | `wp core` | install, update, verify-checksums, version |84| Plugins | `wp plugin` | list, install, activate, update, delete |85| Themes | `wp theme` | list, install, activate, update, delete, `theme mod` |86| Users | `wp user` | create, list, delete (`--reassign`), update, roles, generate |87| Posts / Pages | `wp post` | list, create, update, delete, generate, `post term` |88| Comments | `wp comment` | list, approve, spam, trash, delete |89| Options | `wp option` | get, update, delete, list, `pluck`/`patch` for nested values |90| Cache | `wp cache` / `wp transient` | flush, delete, set, list |91| Cron | `wp cron` | event list/run/schedule/delete, cron test |92| Config | `wp config` | create, get, set, shuffle-salts |93| Multisite | `wp site` | list, create, delete, empty, activate/archive |9495## Workflows9697Full step-by-step workflows live in `references/`, split by task — load only the one the user is doing end-to-end:9899| Workflow | File |100| --- | --- |101| Site migration (local → prod) + search-replace | [`references/examples-migration.md`](references/examples-migration.md) |102| Plugin updates with rollback, debugging, DB maintenance | [`references/examples-maintenance.md`](references/examples-maintenance.md) |103| User audit, cleanup, safe password rotation | [`references/examples-users.md`](references/examples-users.md) |104| Bulk content import from CSV | [`references/examples-content.md`](references/examples-content.md) |105| Multisite / network operations | [`references/examples-multisite.md`](references/examples-multisite.md) |106107## Remote execution108109### Ad-hoc SSH110111```bash112ssh user@example.com "cd /var/www/html && wp plugin list"113```114115### Configured aliases (preferred)116117Define aliases once in `~/.wp-cli/config.yml`:118119```yaml120@prod:121 ssh: user@example.com/var/www/html122@staging:123 ssh: user@staging.example.com/var/www/staging124```125126Then:127128```bash129wp @prod plugin list130wp @staging db export131```132133Aliases beat ad-hoc SSH because they compose with every wp-cli flag (`wp @prod --dry-run search-replace ...`) and don't invite shell-quoting bugs.134135## Common errors136137| Error | Cause | Fix |138| --- | --- | --- |139| `The site you have requested is not installed` | Not at WordPress root / `wp-config.php` missing | `cd` to root or `--path=/path/to/wordpress` |140| `MySQL connection failed` | Bad credentials or MySQL down | Check `wp-config.php`, confirm MySQL is running |141| `Error: Can't select database` | DB doesn't exist | `wp db create` |142| `PHP Fatal error: Allowed memory size exhausted` | Large op or heavy plugins | `php -d memory_limit=512M $(which wp) db export` |143| `This does not seem to be a WordPress installation` | WP files not found | Check directory; confirm WP is actually installed |144| `The \`guid\` column is often used ... but should not be updated` | Expected warning | `--skip-columns=guid` suppresses it; GUIDs are permanent IDs, not URLs |145146## Antipatterns — when wp-cli is the wrong tool147148- **Looping over millions of rows** in shell with `xargs` or `for`. Each `wp` invocation bootstraps WordPress; 1M iterations = days. Prefer a single `wp db query` with a proper SQL statement, or a PHP-side batched job.149- **Using `wp search-replace` without `--precise` on serialized data** larger than a hobby blog. `--precise` is slower but handles PHP serialized strings correctly; without it, serialized arrays silently corrupt.150- **Validation and domain logic.** If the logic belongs inside a WP plugin's hook lifecycle, do it there. wp-cli is for operational work, not business rules.151- **Credential-bearing commands in your shell history.** `wp user create` with `--user_pass=...` leaks. Pipe the password in or let wp-cli prompt.152153## Best practices1541551. **Staging first, always.** Never run an untested command on production.1562. **Track `wp-config.php` changes in version control** when feasible (exclude secrets).1573. **Read the output.** wp-cli warnings often precede data loss.1584. **Document non-obvious workflows** — migrations, multisite conversions — as they happen; future-you will not remember which flags you used.1595. **`--format=json` whenever piping to `jq` or another script.** The default human format rots.1606. **Verify after big changes** — load the homepage, check admin, exercise critical paths.1617. **Keep wp-cli current**: `wp cli update` (Phar installs only — Homebrew and Composer installs update through their package manager).162163## References164165- [`references/commands.md`](references/commands.md) — detailed command + flag reference by category166- Workflow guides (load on demand):167 - [`references/examples-migration.md`](references/examples-migration.md) — migration + search-replace168 - [`references/examples-maintenance.md`](references/examples-maintenance.md) — plugin updates, debugging, DB maintenance169 - [`references/examples-users.md`](references/examples-users.md) — user audit & cleanup170 - [`references/examples-content.md`](references/examples-content.md) — CSV content import171 - [`references/examples-multisite.md`](references/examples-multisite.md) — multisite operations172- Upstream docs: <https://developer.wordpress.org/cli/commands/>