From claude-wp-builder
WP-CLI-first principle — use WP-CLI instead of generating PHP code whenever possible, with command reference, ACF seeding patterns, and environment-aware execution
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-wp-builder:wp-cli-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill teaches the **WP-CLI-first principle**: use WP-CLI commands instead of generating PHP code whenever possible. WP-CLI saves tokens, reduces errors, and executes faster than writing throwaway PHP files.
This skill teaches the WP-CLI-first principle: use WP-CLI commands instead of generating PHP code whenever possible. WP-CLI saves tokens, reduces errors, and executes faster than writing throwaway PHP files.
Always prefer a single WP-CLI command over generating PHP code.
# BAD (costs tokens): Generate PHP file with update_option()
# GOOD (1 line): $WP option update my_option 'value'
# BAD: Generate PHP with wp_insert_post()
# GOOD: $WP post create --post_type=page --post_title='About' --post_status=publish
# BAD: Generate PHP with wp_create_nav_menu()
# GOOD: $WP menu create "Primary EN" && $WP menu item add-post primary-en 5
# BAD: Generate PHP to activate a plugin
# GOOD: $WP plugin activate secure-custom-fields
# BAD: Generate PHP to set permalink structure
# GOOD: $WP rewrite structure '/%postname%/'
Use wp eval only when no dedicated WP-CLI subcommand exists for the operation (e.g., calling ACF's update_field() API).
$WP ConventionThroughout all commands, agents, and skills, $WP is shorthand for the value of wp_cli.wrapper from .wp-create.json. Agents read this value and substitute it into all WP-CLI commands.
For example, if the environment is Docker:
# $WP expands to:
docker exec my-project-wp wp --allow-root
# So this command:
$WP option update blogname "My Site"
# Becomes:
docker exec my-project-wp wp --allow-root option update blogname "My Site"
How to read $WP: Parse .wp-create.json at the project root and extract the wp_cli.wrapper value. Every WP-CLI command in this skill assumes $WP is set to that value.
Agents read wp_cli.wrapper from .wp-create.json and prepend it to all WP-CLI commands. The wrapper value depends on the environment type:
| Environment | wp_cli.wrapper value | Notes |
|---|---|---|
| Native | wp --path=/var/www/html/my-project | Direct CLI, requires WP-CLI installed on host |
| Docker | docker exec my-project-wp wp --allow-root | Executes inside WordPress container |
| DDEV | ddev wp | DDEV proxies to the web container |
| Lando | lando wp | Lando proxies to the appserver container |
| wp-env | npx wp-env run cli wp | wp-env proxies to its CLI container |
Important: Never hardcode the execution method. Always read the wrapper from the manifest so commands work across all environments.
.wp-create.jsonThe .wp-create.json manifest is generated by /wp-create at the project root. It is the single source of truth for all commands, agents, and skills.
# Check if manifest exists
if [ -f .wp-create.json ]; then
# Extract the WP-CLI wrapper
WP=$(jq -r '.wp_cli.wrapper' .wp-create.json)
# Extract other useful values
PROJECT_SLUG=$(jq -r '.project.slug' .wp-create.json)
PRIMARY_LANG=$(jq -r '.languages.primary' .wp-create.json)
ADDITIONAL_LANGS=$(jq -r '.languages.additional[]' .wp-create.json)
ENV_TYPE=$(jq -r '.environment.type' .wp-create.json)
fi
When .wp-create.json does not exist, WP-CLI features are unavailable. Fall back to file-only operations (the pre-existing behavior).
All 16 domains agents should know. Every command below is prefixed with $WP in practice.
| Domain | Commands |
|---|---|
| Database | wp db create, wp db import, wp db export, wp db check, wp db query |
| Content | wp post create, wp post update, wp post delete, wp post meta update |
| Media | wp media import <url>, wp media regenerate |
| Options | wp option get, wp option update, wp option delete |
| Menus | wp menu create, wp menu item add-post, wp menu item add-custom, wp menu location assign |
| Plugins | wp plugin install, wp plugin activate, wp plugin deactivate, wp plugin list |
| Theme | wp theme activate, wp theme list |
| Config | wp config set, wp config get, wp config list |
| Rewrite | wp rewrite structure, wp rewrite flush |
| Cache | wp cache flush, wp transient delete --all |
| Cron | wp cron event list, wp cron event run |
| Search | wp search-replace 'old' 'new' |
| Scaffold | wp scaffold child-theme, wp scaffold plugin |
| Export/Import | wp export, wp import |
| User | wp user create, wp user update |
| Eval | wp eval 'php_code();' |
--porcelain — return only the ID (useful for capturing post/attachment IDs)--format=json — machine-readable output for parsing--format=table — human-readable output for display--allow-root — required inside Docker containers running as root--force — skip confirmation prompts (e.g., wp post delete 1 --force)update_field() via wp evalUse ACF's own API for field operations. This is storage-format-agnostic and handles field key registration, serialization, and caching correctly.
# Simple field on options page
$WP eval "update_field('hero_title', 'Building Digital Excellence', 'option');"
# Image field (import first, use attachment ID)
ID=$($WP media import 'https://images.unsplash.com/photo-xxx' --title='Hero Background' --porcelain)
$WP eval "update_field('hero_image', $ID, 'option');"
# Repeater field
$WP eval "
\$rows = array(
array('title' => 'Web Design', 'description' => 'Custom websites...', 'icon' => 43),
array('title' => 'SEO', 'description' => 'Search optimization...', 'icon' => 44),
);
update_field('services_cards', \$rows, 'option');
"
# Page post meta (field on a specific page)
$WP eval "update_field('about_hero_title', 'Our Story', <post_id>);"
wp_options for Bulk OperationsFaster for bulk seeding but coupled to ACF internals. Use only when ACF API is unavailable or for bulk performance.
ACF stores options page fields in wp_options with an options_ prefix (e.g., field hero_title is stored as options_hero_title).
# Simple field
$WP option update options_hero_title "Building Digital Excellence"
$WP option update options_hero_image 42
# Repeater fields (indexed subfields + count)
$WP option update options_services_cards_0_title "Web Design"
$WP option update options_services_cards_0_icon 43
$WP option update options_services_cards_1_title "SEO"
$WP option update options_services_cards_1_icon 44
$WP option update options_services_cards 2 # total row count
$WP cache flush
This convention matches the i18n helper system defined in the wp-bilingual skill.
hero_title, hero_description, cta_text_<lang>: hero_title_es, hero_description_es, cta_text_es# Primary language (no suffix)
$WP eval "update_field('hero_title', 'Building Digital Excellence', 'option');"
# Secondary language (append _<lang>)
$WP eval "update_field('hero_title_es', 'Construyendo Excelencia Digital', 'option');"
$WP eval "update_field('hero_subtitle_es', 'Creamos sitios web que funcionan', 'option');"
# Bilingual repeater subfields
$WP eval "
\$rows = get_field('services_cards', 'option');
\$rows[0]['title_es'] = 'Diseno Web';
\$rows[1]['title_es'] = 'SEO';
update_field('services_cards', \$rows, 'option');
"
.wp-create.json field languages.additional to know which suffixes to generatelanguages.additional is empty, skip all _<lang> field operationsHOME_ID=$($WP post create --post_type=page --post_title='Home' --post_status=publish --porcelain)
ABOUT_ID=$($WP post create --post_type=page --post_title='About' --post_status=publish --porcelain)
$WP option update show_on_front 'page'
$WP option update page_on_front $HOME_ID
$WP menu create "Primary EN"
$WP menu create "Primary ES"
$WP menu item add-post primary-en $HOME_ID --title="Home"
$WP menu item add-post primary-en $ABOUT_ID --title="About"
$WP menu item add-post primary-es $HOME_ID --title="Inicio"
$WP menu item add-post primary-es $ABOUT_ID --title="Acerca"
$WP menu location assign "Primary EN" primary_en
$WP menu location assign "Primary ES" primary_es
ID=$($WP media import 'https://example.com/photo.jpg' --title='Hero Image' --porcelain)
$WP eval "update_field('hero_image', $ID, 'option');"
# Verify a field was seeded
$WP eval "echo get_field('hero_title', 'option') ? 'OK' : 'EMPTY';"
# Verify a page exists with correct template
$WP eval "echo get_page_template_slug($PAGE_ID);"
# Verify plugin is active
$WP plugin list --status=active --format=table
# Verify menus are assigned
$WP menu location list --format=table
$WP rewrite flush
$WP cache flush
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub yojahny55/claude-wp-builder --plugin claude-wp-builder