wp-cli/package-command
Lists, installs, and removes WP-CLI packages, community-maintained projects that extend WP-CLI. Commands include wp package list, wp package install (via Composer), wp package uninstall, and wp package browse to explore available packages.
Architecture fit — the framing needs correcting first. wp-cli/package-command is not a Laravel or general-purpose PHP package. It is a WP-CLI bundle command: a set of command classes (package browse|get|install|is-installed|list|path|update|uninstall) that are discovered and executed by the WP-CLI runtime. There is no service provider, no facade, no stable PHP API to use from application code, and no standalone entry point. Its "architecture" is a thin orchestration layer that rewrites ~/.wp-cli/packages/composer.json and shells into Composer to resolve and install WP-CLI extensions.
That means the realistic leverage model for a Laravel/PHP product team is process invocation, not library adoption: a Laravel-based control plane, provisioning pipeline, or internal ops CLI calls the wp binary and consumes exit codes and --format=json output. The value is in the contract (deterministic exit codes, machine-readable formats, idempotent install/uninstall semantics), not in the code. Treat it as an operator-facing capability you inherit from WP-CLI, not a dependency you build on.
Integration feasibility: high, but only via the CLI boundary. Favorable signals:
--format=json|csv|yaml|ids, and is-installed returns exit code 0/1 — exactly what a wrapper needs for scripting and assertions.install/update/uninstall accept --no-interaction, explicitly documented as "useful for scripting."WP_CLI_PACKAGES_DIR allows redirecting the package root out of $HOME, which is the single most important knob for automation and multi-tenancy.testing.yml), and a documented Behat/PHPUnit/PHPStan contributor workflow.Unfavorable: the package index backing wp package browse is explicitly deprecated and will not be updated, so the discovery half of the surface is already legacy. The install/update half depends on live Composer + GitHub reachability.
Technical risk (ranked).
| Risk | Severity | Notes |
|---|---|---|
| Package index deprecated | High for browse only |
README directs to wp-cli/ideas#51; no replacement shipped. Do not build a "package catalog" feature on it. |
| No stable PHP API | High | Classes are coupled to WP-CLI internals; refactors upstream are not semver-guarded for you. Forking or subclassing is a trap. |
Global-ish state in ~/.wp-cli/packages/ |
High | Shared users on shared hosts collide. Must set WP_CLI_PACKAGES_DIR per project/tenant. |
| Low external validation | Medium | 18 stars, 0 dependents, score 25.9. Signal is "core-bundled and unremarked," not "widely battle-tested third party." You get WP-CLI's own test suite, not a broad user base. |
| Composer SAT resolution + network in the install path | Medium | Non-hermetic; needs network, GitHub auth, and produces a composer.lock you must capture. |
--insecure flag |
Medium | TLS-bypass escape hatch will be reached for during incidents; must be blocked or gated in policy. |
| Auth/rate limits | Medium | GitHub PAT required in some cases (composer config -g github-oauth.github.com); tokens expire and hit rate limits. |
--skip-update-check drift |
Low | Convenient but means list output is not a drift signal unless you omit it. |
Key questions for the team before committing.
browse at all? If not, the deprecated index risk drops to zero.WP_CLI_PACKAGES_DIR policy and whether we can commit a lockfile.)wp package install from an arbitrary Git URL or zip is remote code into a WP-CLI runtime — approval gating is a security control, not bureaucracy.Stack fit. PHP 7.4/8.x, Composer 2.x, WP-CLI runtime — all compatible with a Laravel shop's toolchain at the language and binary level, and entirely incompatible at the framework level. No Artisan command, no container binding, no event hooks. If the goal is "manage WP-CLI extensions from our Laravel admin," the correct shape is a queued job that runs wp package <verb> --no-interaction --format=json via Symfony\Process, parses JSON, and persists the result — with wp and the WP-CLI packages directory treated as external, pinned infrastructure.
Migration path.
wp package is present (wp package --help). Version-bump WP-CLI rather than installing this package separately.package-command is genuinely needed, note the documented self-hosting path — wp package install [email protected]:wp-cli/package-command.git — which replaces the bundled copy. Record this as a deliberate, versioned deviation, not an incidental side effect.WP_CLI_PACKAGES_DIR (per project or per repo checkout) so state is reproducible and diffable.composer.json + composer.lock from the packages directory; restore from it in CI instead of re-resolving.browse from any planned UX and replace with an allow-listed manifest of approved package names + version constraints that you own.wp-packages:sync / wp-packages:audit commands with allow-lists, --no-interaction always, and --insecure hard-blocked.Compatibility. Constraint ranges (:2.0.0, :^1.0, :~1.2, :@stable, :dev-main) are passed through to Composer, so a caret constraint in a production environment means a package can change behavior without your code changing. Prefer exact pins or committed lockfiles for anything operationally load-bearing. Local-directory installs register a reference only — "if you move or delete the directory, WP-CLI's reference breaks" — so never use path installs in deployed environments; use git tags or zips.
Sequencing recommendation. Ship read-only leverage first (package list --format=json, package is-installed, package path) as an inventory/observability feature — near-zero risk, no code execution. Gate mutating verbs (install, update, uninstall) behind a reviewed allow-list and a change ticket. Never expose raw package names or URLs from user input into install.
Maintenance. Upstream maintenance burden is effectively WP-CLI's release cadence, not this repo's — it is core-bundled, MIT, with CI on every PR and a documented contributor workflow (WP_CLI_CS via PHP_CodeSniffer, PHPStan, Behat). But 18 stars / 0 dependents means you are the QA for your usage pattern. Budget for reading release notes on WP-CLI bumps; do not expect a compatibility shim for internal API changes you might inadvertently rely on.
Support. Routing is community: WP-CLI GitHub issues, the handbook, the Commands Cookbook. No vendor, no SLA. Internalize this: your team owns first-line support for anything you expose to users. Contributing back is realistic and cheap (small codebase, clear conventions, composer phpcs / phpstan / behat -- features/<file> inner loop) — but note composer test runs the full Behat suite, which installs WordPress per scenario, takes tens of minutes, and requires MySQL/MariaDB, jq, and network access to WordPress.org. That is a real CI cost and a reason not to fork.
Scaling. The scaling unit is the packages directory and the Composer resolution, not request throughput.
~/.wp-cli/packages/ is per-user global state — on shared or multi-tenant hosts this is a correctness bug, not a performance one. WP_CLI_PACKAGES_DIR is mandatory at scale.--skip-update-check materially reduces latency and auth exposure for list on large fleets, at the cost of losing drift visibility — choose per endpoint.Failure modes to design for.
--insecure → MITM exposure. Make this a blocked flag with a documented alternative (fix the CA bundle, use a mirror).composer.json, resolves, then generates autoload files; interruption leaves a half-installed tree. Recovery must be "restore pinned lockfile and reinstall," scripted.^/dev-main constraint resolves forward on wp package update. Combined with update with no arguments ("all packages"), a routine maintenance command can change CLI behavior fleet-wide.browse reads a deprecated index; treat its output as historical, never as a source of truth.is-installed returns 1 for "not installed," which naive set -e scripts misread as failure. Your wrapper must special-case it.Ramp-up. Operator-level use (list, get, path, is-installed) is minutes — the README examples are directly copy-pasteable. Automation-level use (safe install/update in CI, allow-lists, lockfile discipline, PAT management, per-tenant isolation) is a few days including the security review, mostly spent on the wrapper and policy rather than the tool. Contributor-level use is realistic for one engineer given the small surface and clear conventions — but the full test suite's MySQL + WordPress-install-per-scenario requirements make it a deliberate investment, not a casual one, and should be justified by a specific upstream fix you need rather than general hygiene.
Bottom line for the roadmap. Adopt as an inherited, wrapped CLI capability for WP-CLI extension inventory and controlled installation — not as a dependency, not as a library, and not as the basis of a package-catalog feature (browse is deprecated, index is frozen). The opportunity score of 62.8 reflects "valuable capability, thin package," which is exactly right: the value is already delivered to you by WP-CLI, and this repo's job is to not get in the way. Keep your investment in the wrapper, the allow-list, and the lockfile — that is where the durable engineering work sits.
How can I help you explore Laravel packages today?