christophrumpel/laravel-command-file-picker
A small Laravel package that adds a file picker for Artisan command files, letting you browse and select existing command classes instead of typing file paths by hand. Handy for quickly locating and working with commands in your app.
composer require christophrumpel/laravel-command-file-picker
There is no service provider or config to publish — the package ships one or more traits you add to an existing Artisan command. The whole API surface is small, so the fastest path is:
src/ — each picker is a thin wrapper over Symfony Console's ChoiceQuestion, so behaviour is predictable.First use case: an existing generator or import command that currently takes a hardcoded path or requires the user to type a class name. Add the trait and let the user pick:
use ChristophRumpel\LaravelCommandFilePicker\Traits\PicksFiles;
use Illuminate\Console\Command;
class ImportPostsCommand extends Command
{
use PicksFiles;
protected $signature = 'import:posts {file?}';
public function handle()
{
$file = $this->argument('file')
?? $this->pickFile(base_path('imports'));
$this->info("Importing {$file}");
}
}
You get an arrow-key / numeric selection list of the files in that directory instead of a blank prompt. The class-picker variant does the same over the classes in a namespace, which is where it earns its keep.
Argument-or-pick (the pattern you'll use 90% of the time). Always keep the positional argument and fall back to the picker only when it's missing. This keeps the command scriptable and testable while making it pleasant to run by hand:
$model = $this->argument('model') ?: $this->pickClass('App\\Models');
Pick-then-resolve via the container. For the class picker, resolve the chosen FQCN through Laravel so constructor injection keeps working:
$importer = app($this->pickClass('App\\Importers'));
$importer->handle();
Small wizards. Chain pickers to turn a clunky command into a guided flow — pick a directory, then a file, then a driver class — but gate the whole thing behind $this->option('no-interaction') checks so CI doesn't hang.
Constraining the list. Point the file picker at a narrow directory (database/seeders, config, resources/stubs) rather than the project root. Narrow directories are also where the picker stays fast and readable — a few dozen entries, not a few thousand.
Testing. Do not try to test the picker itself; test your command with the argument supplied. $this->artisan('import:posts', ['file' => 'posts.csv'])->assertExitCode(0) bypasses the prompt entirely because of the argument-first pattern above. If you must exercise the interactive path, use Laravel's expectsChoice() / expectsQuestion().
Composition with your own prompts. The picker returns a plain string (path or FQCN), so it composes cleanly with $this->ask(), $this->confirm() and friends — no special handling required.
--no-interaction, CI runners, and piping output all disable the underlying Symfony question. Guard every picker call behind a supplied argument or a $this->input->isInteractive() check.base_path() vs app_path(). The file picker resolves relative to whatever path you hand it. Pass absolute paths derived from a helper (app_path(), base_path(), config_path()) — a bare 'Models' will resolve against the process CWD, which is public/ under some web-triggered invocations.files in composer.json) won't show up. composer dump-autoload first, then check the namespace is a real PSR-4 root..gitkeep, and large vendor-adjacent folders all show up unless you point at a tight directory. The package does no filtering for you.select(), search(), suggest() and better UX out of the box. If you're on a modern Laravel, consider Prompts for new code and treat this package as a lightweight dependency for older apps. Its last release was Dec 2020, so pin it and don't expect fixes.How can I help you explore Laravel packages today?