wp-cli/scaffold-package-command
A WP-CLI command that scaffolds new WP-CLI packages, generating the package structure, command class, tests, README, and supporting files so developers can quickly start building custom WP-CLI commands.
Install the scaffolder
# Global, for use across all your WordPress projects
wp package install wp-cli/scaffold-package-command
# Or per-project, as a dev dependency
composer require --dev wp-cli/scaffold-package-command
This package is a WP-CLI command in its own right, so it does not need a WordPress install to run — a local wp binary is enough.
Verify and explore
wp scaffold package --help
wp scaffold package-readme --help
wp scaffold package-tests --help
wp scaffold package-github --help
First use case — you have a Laravel app that also boots WordPress (Corcel, a sidecar WP install, or a headless setup) and you keep hand-rolling wp eval-file hacks for maintenance jobs. Turn one of those into a real, tested command package:
wp scaffold package acme-maintenance \
--dir=packages \
--ci=github \
--meta="name=Acme Maintenance" \
--meta="description=Cache and cron maintenance commands for Acme"
Look at these files first (in this order):
acme-maintenance-command.php — the WP-CLI entry point and WP_CLI::add_command() call.features/acme-maintenance.feature — the behavior contract; this is what you extend.tests/test-acme-maintenance.php — PHPUnit unit tests.README.md — generated from package-readme; regenerate rather than hand-editing the header.composer.json / phpunit.xml.dist / behat.yml / .github/workflows/ — the tooling wiring.Then:
cd packages/acme-maintenance
composer install
composer test # or vendor/bin/phpunit / vendor/bin/behat
Flesh out the stub command class. The generated <name>-command.php declares a class with a __invoke() and a placeholder method. Add subcommands with the standard annotation vocabulary rather than branching on $args:
class Acme_Maintenance_Command {
/**
* Flush object cache and expired transients.
*
* ## OPTIONS
* [--dry-run]
* : Report what would be removed.
*
* ## EXAMPLES
* wp acme-maintenance flush --dry-run
*
* @subcommand flush
*/
public function flush( $args, $assoc_args ) { /* ... */ }
}
Annotations (@subcommand, @when, @synopsis, @alias) are the idiomatic extension point — don't hand-parse $args.
Drive feature files, not just unit tests. The generated features/*.feature is the right place to assert CLI-observable behavior (exit codes, output, --format=json). A typical loop:
Scenario in features/acme-maintenance.feature.vendor/bin/behat features/acme-maintenance.feature.Use --meta for package identity. --meta="name=...", --meta="description=..." flow into composer.json, the README header, and the docblock. Setting them at scaffold time avoids a find-and-replace pass later.
Regenerate rather than hand-edit boilerplate. After adding a new subcommand or changing dependencies:
wp scaffold package-readme acme-maintenance --dir=packages/acme-maintenance
wp scaffold package-tests acme-maintenance --dir=packages/acme-maintenance
Keep README.md's usage block and the GitHub workflow in sync with the code by re-running these; treat the generated prose as derived, not authored.
Wire it into a Laravel project. The clean pattern is a Composer path repository, not wp package install (which drops the package into ~/.wp-cli/packages and is invisible to your app's lockfile):
{
"repositories": [{ "type": "path", "url": "packages/acme-maintenance" }],
"require-dev": { "acme/maintenance": "*" }
}
Then register it via a project wp-cli.yml:
path: wordpress
require:
- vendor/acme/maintenance/acme-maintenance-command.php
This keeps the command versioned with the app, testable in CI, and loadable whether or not the global WP-CLI package directory exists.
Scaffold GitHub hygiene once per package. wp scaffold package-github <name> emits issue/PR templates, CONTRIBUTING.md, and (depending on version) a workflow. Run it at package creation, not after your first PR.
Prototype fast, then backfill. For throwaway scripts use --skip-tests; before the package leaves your machine, re-run wp scaffold package-tests <name> --dir=... to restore the PHPUnit + Behat scaffolding rather than writing it by hand.
wp scaffold package derives the directory, the *-command.php filename, and the PHP class from the argument. Use lowercase, dash-separated names (acme-maintenance), not AcmeMaintenance or acme/maintenance; vendor prefixes go in composer.json, not the argument.package-readme and package-tests are your update paths, and --force (where supported) should be used deliberately.tests/bootstrap.php expects WP_TESTS_DIR (and friends like WP_CORE_DIR) to point at a checked-out wordpress-develop tests directory, plus a reachable MySQL. If composer test dies on bootstrap, set the env var before blaming your code. Pure-logic tests can avoid this by not extending the WP test case.wp binary on PATH. behat.yml invokes WP-CLI directly; CI failures here are almost always a missing wp or a WP_CLI_PACKAGES_DIR pointing somewhere unexpected.wp-cli/wp-cli-bundle. If your Laravel app already requires the bundle, a scaffolded package's wp-cli/wp-cli constraint can clash. Install it --dev, or use --no-update and reconcile versions manually.wp package install puts code in ~/.wp-cli/packages/vendor/...; files there are outside your repo and your lockfile. Debug with wp --debug and check WP_CLI_PACKAGES_DIR when a command "isn't found."Acme_Maintenance_Command). Two packages scaffolding similar names will collide; rename the class before you ship.package-readme writes img.shields.io and Packagist-style links from the package name. If the repo lives elsewhere, edit the badge block after generation and expect to redo it on every regeneration.wp scaffold does not need WordPress. Because it only writes files, you can run it from a Laravel repo root, a monorepo packages/ folder, or CI — don't over-engineer a WP environment just to scaffold.package-readme and package-tests will rewrite their targets; commit before running them so the diff is reviewable, and keep custom sections outside the generated markers where the tooling allows.--ci support before assuming GitHub Actions. The --ci values vary by version; run wp scaffold package --help and confirm github is accepted, otherwise you'll get Travis-era files or none at all.How can I help you explore Laravel packages today?