Foundation CLI
Foundation CLI is development tooling for generating project code. It reads the consuming project’s Composer configuration, follows WordPress naming and formatting conventions, and uses stubs owned by the runtime package that defines each generated API.
Installation
Section titled “Installation”Install the CLI as a development dependency in a consuming project:
List its available commands:
Do not register StellarWP\Foundation\Cli\CliProvider in the WordPress application’s provider list. It boots the Symfony Console application for the foundation executable and is unrelated to WordPress request bootstrap.
Add a Composer script
Section titled “Add a Composer script”The binary can be exposed through a project script in composer.json:
Pass command arguments after --:
Generate project code
Section titled “Generate project code”Generate a WP-CLI command
Section titled “Generate a WP-CLI command”The generated class extends Foundation’s WP-CLI command base and demonstrates positional arguments, associative options, and flags. A command shipped by the plugin requires the runtime package:
Generate a database feature
Section titled “Generate a database feature”Generate the application provider first so later generators can register the table and migration automatically:
The --migration flag creates and registers the table’s initial migration in the same operation. Table --namespace and --path options affect only the table class; the migration uses its project-configured namespace and path, falling back to the Foundation convention. Use an explicit identifier such as --migration-id=2026_09_04_143200_create_reports_table only when the generated timestamp identifier must be replaced. The explicit ID determines that migration’s position in Foundation’s ascending execution order.
Use --table-name=<name> when the table’s unprefixed WordPress name should differ from the name derived from its class. On make:database-migration, --table=<class> instead selects the existing table class that an alteration migration changes.
Generated database classes require the runtime package:
Database table and migration generators refuse to overwrite existing files. Edit an unapplied migration directly, or create a new migration after the existing one has been deployed.
The migration generator selects one of three modes:
| Invocation | Generated behavior | Override stub |
|---|---|---|
make:database-migration Create_Reports_Table --create=Reports_Table |
Creates the explicitly selected table; down() drops the table |
create-table-migration.stub |
make:database-migration Add_Status_To_Reports --table=Reports_Table |
Starts an explicit alteration blueprint; down() throws IrreversibleMigration until replaced |
alter-table-migration.stub |
make:database-migration Backfill_Report_Status |
Generates a generic migration; add service or table dependencies manually | migration.stub |
The --create and --table options are mutually exclusive. Each accepts a short class name from the configured table namespace (Database\\Tables by default), or a fully qualified table class. Migration names never select destructive behavior by themselves.
Inspect command options
Section titled “Inspect command options”Use Symfony Console’s built-in help for supported names, paths, namespaces, and feature-specific options:
Without project overrides, generators use the first autoload.psr-4 entry in the project’s composer.json as their root namespace. Output directories follow the most specific Composer mapping for the selected namespace. Explicit --namespace and --path options override those defaults.
Customize generation
Section titled “Customize generation”Configure generator locations
Section titled “Configure generator locations”Set project-wide namespaces in foundation/config.php. The file is optional; include only the generators whose defaults you want to change:
Run Foundation from the project root. It loads this file once when the CLI starts; use a PHP array that can be loaded independently of WordPress.
Namespaces are fully qualified application namespaces. With Plugin\\ mapped to src/ in Composer, the example produces:
| Generator | Directory |
|---|---|
make:wpcli-command |
src/Commands/ |
make:database-provider |
src/Persistence/ |
make:database-table |
src/Persistence/Tables/ |
make:database-migration |
src/Persistence/Migrations/ |
An explicit --namespace takes precedence over the configured namespace. Without either, the conventional suffixes are Cli\\Commands, Database, Database\\Tables, and Database\\Migrations, respectively. An explicit --path chooses the output directory for that invocation; it does not change the namespace or update Composer autoload mappings.
Table and migration generators look for Provider.php in the configured database-provider namespace. Generate that provider first to enable automatic registration. Use --provider to select another provider file, including a provider generated with a custom class name. Paired migrations from make:database-table --migration use the migration setting independently of table options; short --create and --table references use the table setting.
Invalid namespace settings for registered generators or missing Composer mappings stop generation before files are written. Correct the setting or mapping before retrying. If the provider namespace has no mapping, correct generators.database-provider.namespace or its Composer mapping, or select an existing provider file with --provider.
If Foundation scaffolding should be excluded from your production archive, add foundation/ to the project’s .gitattributes export exclusions.
Create a custom generator
Section titled “Create a custom generator”Follow Create a custom generator for a complete command, provider, stub, and executable. The command owns its CONFIG_KEY and NAME; its provider references that constant when contributing namespace defaults.
Override package stubs
Section titled “Override package stubs”Place project-specific stubs under foundation/stubs/ using the same feature path as the package default:
Copy the package’s default stub before customizing it so required placeholders remain available. Local scaffolding assets that should not ship in a production zip should be excluded in the consuming project’s .gitattributes.
Generate Strauss-compatible imports
Section titled “Generate Strauss-compatible imports”When the consuming project’s composer.json defines extra.strauss.namespace_prefix, generators apply that prefix to Foundation imports. For example, a configured Plugin\\ prefix changes:
to:
This keeps generated classes compatible when Strauss prefixes dependencies without updating project call sites. Handwritten imports remain the application’s responsibility.
Build a project-specific CLI
Section titled “Build a project-specific CLI”The installed vendor/bin/foundation executable contains Foundation’s commands. A project that needs its own Symfony Console commands can create a separate executable using StellarWP\Foundation\Cli\Application.
In src/Cli/Cache_Clear_Command.php:
In src/Cli/Command_Provider.php:
Then create the project’s executable, for example bin/your-plugin:
This small example has no application dependencies. When commands need services, construct the command provider through the project’s container instead of creating dependencies inside command classes.
Foundation monorepo maintenance
Section titled “Foundation monorepo maintenance”Select an existing package by its directory (src/Log), short name (Log), repository name (foundation-log), or full manifest name (stellarwp/foundation-log). The Docs package is also discovered from its package.json and accepts Docs, src/Docs, foundation-docs, or @stellarwp/foundation-docs.
Each directory represents one split package. When both manifests exist, composer.json determines the package identity for this command and the split workflow; package.json is used only when composer.json is absent.
Invalid JSON in the selected manifest stops discovery. Correct the file before retrying.
Preview the repository actions without changing GitHub:
The generated actions disable issues, wikis, projects, and pull requests on the split repository.
Pass --apply only after reviewing the generated actions:
For a new PHP package, pass its short name. The command asks for confirmation before creating src/<Package> scaffolding, prompts for the Composer package name, and runs composer monorepo merge after local package creation.