Skip to content

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.

Install the CLI as a development dependency in a consuming project:

composer require --dev stellarwp/foundation-cli

List its available commands:

vendor/bin/foundation list

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.

The binary can be exposed through a project script in composer.json:

{
  "scripts": {
    "foundation": "@php vendor/bin/foundation"
  }
}

Pass command arguments after --:

composer run foundation -- list
vendor/bin/foundation make:wpcli-command Sync_Products_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:

composer require stellarwp/foundation-wpcli

Generate the application provider first so later generators can register the table and migration automatically:

vendor/bin/foundation make:database-provider
vendor/bin/foundation make:database-table Reports_Table
vendor/bin/foundation make:database-migration Create_Reports_Table

Generated database classes require the runtime package:

composer require stellarwp/foundation-database

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.

Use Symfony Console’s built-in help for supported names, paths, namespaces, and feature-specific options:

vendor/bin/foundation help make:wpcli-command
vendor/bin/foundation help make:database-provider
vendor/bin/foundation help make:database-table
vendor/bin/foundation help make:database-migration

The generators use the first autoload.psr-4 entry in the project’s composer.json to determine the default namespace and source path. Explicit --namespace and --path options override those defaults.

Place project-specific stubs under foundation/stubs/ using the same feature path as the package default:

foundation/stubs/
  wpcli/
    command.stub
  database/
    provider.stub
    table.stub
    table-migration.stub
    migration.stub

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.

When the consuming project’s composer.json defines extra.strauss.namespace_prefix, generators apply that prefix to Foundation imports. For example, a configured YourPlugin\\ prefix changes:

use StellarWP\Foundation\WPCli\Command;

to:

use YourPlugin\StellarWP\Foundation\WPCli\Command;

This keeps generated classes compatible when Strauss prefixes dependencies without updating project call sites. Handwritten imports remain the application’s responsibility.

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:

<?php declare(strict_types=1);

namespace YourPlugin\Cli;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

final class Cache_Clear_Command extends Command {

	public function __construct() {
		parent::__construct( 'cache:clear' );
	}

	protected function execute( InputInterface $input, OutputInterface $output ): int {
		$output->writeln( 'Cache cleared.' );

		return Command::SUCCESS;
	}
}

In src/Cli/Command_Provider.php:

<?php declare(strict_types=1);

namespace YourPlugin\Cli;

use StellarWP\Foundation\Cli\Contracts\CommandProvider;

final class Command_Provider implements CommandProvider {

	public function __construct(
		private readonly Cache_Clear_Command $command
	) {
	}

	public function commands(): iterable {
		yield $this->command;
	}
}

Then create the project’s executable, for example bin/your-plugin:

#!/usr/bin/env php
<?php declare(strict_types=1);

use StellarWP\Foundation\Cli\Application;
use YourPlugin\Cli\Cache_Clear_Command;
use YourPlugin\Cli\Command_Provider;

require dirname( __DIR__ ) . '/vendor/autoload.php';

$application = new Application( commandProviders: [
	new Command_Provider( new Cache_Clear_Command() ),
] );

exit( $application->run() );

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.

Preview the repository actions without changing GitHub:

composer run foundation -- package:create Log

Pass --apply only after reviewing the generated actions:

composer run foundation -- package:create Log --apply

The command can create src/<Package> scaffolding, asks for the Composer package name, and runs composer monorepo merge after local package creation.