Use this skill to create, update, or manage WordPress posts, pages, and custom post types via WP-CLI over SSH. Triggers whenever the user wants to publish, edit, update, or manage content on a WordPress site — including standard posts, pages, and custom post types (CPTs). Also covers media uploads and multisite (subfolder or subdomain) scenarios. Always use this skill when the user mentions WP CLI, WordPress content management, updating a WordPress page or post, uploading images to WordPress, or managing WordPress from the command line, even if they don't explicitly say "WP CLI" or "skill".
Manage WordPress content (posts, pages, custom post types) using WP-CLI over SSH.
Before running any WP-CLI command, confirm SSH access is configured and working.
Run a connectivity test:
wp @<alias> cli version
If this fails, guide the user to set up ~/.wp-cli/config.yml:
@production:
ssh: [email protected]
path: /var/www/html
For AWS Elastic Beanstalk or key-based auth, use:
@production:
ssh: [email protected] --ssh-keys=/path/to/key.pem
path: /var/www/html
Or via ~/.ssh/config (cleaner):
Host wp-production
HostName your-server.com
User ec2-user
IdentityFile ~/.ssh/your-key.pem
Then reference in config.yml:
@production:
ssh: wp-production
path: /var/www/html
Do not proceed until SSH connectivity is confirmed.
Before doing anything, collect the necessary context based on the task:
--url flag (for multisite) from the provided URL. See Multisite Handling below.wp @<alias> post url-to-id <url> [--url=<subsite-url>]
Or by listing posts filtered by slug:
wp @<alias> post list --post_type=<type> --name=<slug> --fields=ID,post_title,post_status --format=json [--url=<subsite-url>]
If you get no results with post, try page or ask the user for the post type.
Always retrieve the existing content before making any changes:
wp @<alias> post get <ID> --fields=post_title,post_content,post_excerpt,post_status --format=json [--url=<subsite-url>]
For meta/custom fields (e.g. ACF):
wp @<alias> post meta get <ID> <meta_key> --format=json [--url=<subsite-url>]
Apply the requested changes locally first (in your working context), and make only the minimum changes requested — no reformatting, no unsolicited additions, no structural changes.
If the update involves new images or files, upload media before patching the post.
Upload from local path:
wp @<alias> media import /path/to/file.jpg --porcelain [--url=<subsite-url>]
Upload from a URL:
wp @<alias> media import https://example.com/image.jpg --porcelain [--url=<subsite-url>]
Attach to a post and set as featured image:
wp @<alias> media import image.jpg --post_id=<ID> --featured_image --porcelain [--url=<subsite-url>]
After import with --porcelain, WP-CLI outputs the plain attachment ID. To get the attachment URL (to use in post content), run:
wp @<alias> post get <ATTACHMENT_ID> --field=guid [--url=<subsite-url>]
wp @<alias> post update <ID> \
--post_title="Updated Title" \
--post_content="Updated content here" \
[--url=<subsite-url>]
wp @<alias> post meta update <ID> <meta_key> "<new_value>" [--url=<subsite-url>]
wp @<alias> post create \
--post_type=<type> \
--post_title="Title" \
--post_content="Content" \
--post_status=publish \
--porcelain \
[--url=<subsite-url>]
⚠️ Make the smallest requested change only. Do not alter formatting, structure, or any content that wasn't explicitly requested.
After updating or creating, fetch the live URL using web_fetch (or take a screenshot if available) to visually verify the changes:
Always check the provided URL to determine if this is a multisite setup.
| Setup type | Example URL | --url flag |
|---|---|---|
| Single site | https://example.com/about | Not needed |
| Subfolder multisite | https://example.com/shop/about | --url=https://example.com/shop |
| Subdomain multisite | https://shop.example.com/about | --url=https://shop.example.com |
For create tasks, if the site is a multisite and the target sub-site isn't mentioned, ask explicitly before proceeding.
WP-CLI treats CPTs identically to posts/pages — just pass --post_type=<cpt_slug>.
For updates: If the post type cannot be inferred from the URL (most slugs don't expose this), ask the user directly:
"What post type is this? (e.g.,
post,page,product,event, etc.)"
For creates: If the user hasn't specified a post type, ask:
"What post type should this be created as?"
Do not assume post or page as a fallback for CPTs.
List all registered CPTs on the site (useful for reference):
wp @<alias> post-type list --fields=name,label --format=json [--url=<subsite-url>]
| Task | Command |
|---|---|
| Get post by URL | wp post url-to-id <url> |
| Get post content | wp post get <ID> --fields=post_content --format=json |
| List posts by type | wp post list --post_type=<type> --fields=ID,post_title --format=json |
| Update post | wp post update <ID> --post_title="..." --post_content="..." |
| Create post | wp post create --post_type=<type> --post_title="..." --post_status=publish --porcelain |
| Import media | wp media import <file-or-url> --porcelain |
| Set featured image | wp media import <file> --post_id=<ID> --featured_image --porcelain |
| Get meta field | wp post meta get <ID> <key> --format=json |
| Update meta field | wp post meta update <ID> <key> "<value>" |
| List sub-sites | wp site list --fields=blog_id,url --format=json |
| List CPTs | wp post-type list --fields=name,label --format=json |