leafs/seedling — a console framework for PHP
One command scaffolds a console app with commands, interactive prompts, styled output and argument parsing — and what you build installs like any real binary: vendor/bin/your-tool.
class GreetCommand extends Command
{
protected $signature = 'greet
{name : The name of the person}
{--greeting=Hello : The greeting to use}';
protected $description = 'Greet someone properly';
protected function handle(): int
{
$this->info("{$this->option('greeting')}, {$this->argument('name')}!");
return 0;
}
}
~/greeter
➜
Hi, Mika!
➜ php leaf greet --help
Greet someone properly
Usage: greet [name] [--options...]
name: The name of the person
--greeting: The greeting to use
➜ echo $?
0 // honest exit codes, always
// proven in production
leaf
Leaf CLI
Scaffolds, serves and manages every Leaf app
alchemy
Alchemy
Tests, style, refactors and CI from one file
php leaf
Leaf MVC console
Generators, migrations, scaffolds — all of it
your-tool
Your tool 🌱
The next thing your terminal can't live without
// batteries, no bloat
Seedling stays out of your way: a handful of primitives that compose, instead of a framework you fight.
Interactive prompts
Text, confirm and select prompts with defaults and inline validation — arrow keys and Windows support included.
? Pick your coffee
❯ Espresso
Cortado
Styled output
info(), error(), comment() and inline markup tags — consistent color on every terminal.
✔ Build finished in 1.2s
// 2 files skipped
✗ deploy failed: missing token
Run real processes
sprout()->run() plus composer and npm wrappers — built on the battle-tested symfony/process.
sprout()->composer()
->install();
Honest exit codes
Handlers return ints and the app propagates them — your tool plays correctly with CI, pipes and shell scripts from day one.
Generators included
php leaf g:command scaffolds commands into app/console, auto-registered — no manual wiring.
A real binary at the end
Publish to Packagist and users run vendor/bin/your-tool — or install globally and call it like any system command.
// ask like you mean it
The same prompt engine that powers leaf create is yours: chain questions, validate inline, branch on answers — declared as plain arrays, no builder gymnastics.
$answers = sprout()->prompt([[
'type' => 'select',
'name' => 'flavor',
'message' => 'Pick your coffee',
'choices' => [
['title' => 'Espresso', 'value' => 'esp'],
['title' => 'Cortado', 'value' => 'cor'],
],
]]);
? Pick your coffee · use arrows
❯ Espresso
Cortado
? Project name (my-tool) brew-buddy
? Initialize git? Yes / no
✔ brew-buddy is ready
// the engine underneath
Seedling is a thin, structured layer over Sprout — a tiny console engine with exactly two dependencies: symfony/process for shelling out and Leaf's own fs for files. No framework baggage, nothing else in your tree.
Building a library and just need to expose a few commands? Skip the framework and require the engine directly.
Sprout on GitHub →embed commands in any package
# the engine, standalone
composer require leafs/sprout
# a command, functional style
$app->command('cache:clear', function () {
$this->info('Cache cleared!');
});
You're one command away from a working console app — and an afternoon away from shipping it.
get started
➜ leaf create my-tool --console
# or: composer create-project leafs/seedling my-tool