bamarni/symfony-console-autocomplete
Adds shell autocompletion for Symfony Console commands, so pressing Tab completes command names, arguments, and options in bash, zsh, and fish. Works with any Symfony Console app, including Composer-installed tools like PHPUnit or Composer itself.
Minimal path from zero to working tab-completion:
_complete/completion machinery. If your framework already exposes it (some Laravel versions do), use that instead. This package is most useful for custom/legacy Symfony Console binaries and tooling that predates native completion.composer require --dev bamarni/symfony-console-autocomplete
# or
composer global require bamarni/symfony-console-autocomplete
Symfony\Component\Console\Application instance:
#!/usr/bin/env php
<?php
require __DIR__.'/vendor/autoload.php';
use Symfony\Component\Console\Application;
use Bamarni\Composer\Autocomplete\Command\CompletionCommand;
$app = new Application('my-cli');
$app->add(new CompletionCommand());
$app->run();
_completion --help to confirm exact flag names in your installed version):
# bash — put in ~/.bashrc
source <(./bin/my-cli _completion --generate-hook --shell-type=bash)
# zsh — put in ~/.zshrc after compinit
source <(./bin/my-cli _completion --generate-hook --shell-type=zsh)
# fish — put in ~/.config/fish/config.fish
./bin/my-cli _completion --generate-hook --shell-type=fish | source
./bin/my-cli <TAB> and confirm command names appear; then try an option: ./bin/my-cli my:command --<TAB>.Where to look first:
Bamarni\Composer\Autocomplete\Command\CompletionCommand source — configure() shows supported options (--generate-hook, --shell-type, program name, etc.); execute() shows how input lines are parsed into suggestions.First useful case: a custom internal CLI (deploy scripts, code generators, data importers) sharing a Symfony Application. One registration gives discoverable command names, option names, and shortcuts across bash/zsh/fish with no per-command boilerplate.
Standalone Symfony Console app (most common). Add the command once in your application bootstrap. Consider registering it conditionally so it is absent from end-user help output but still usable by developers:
if (getenv('APP_ENV') === 'local') {
$app->add(new CompletionCommand());
}
Laravel / Artisan integration. Register via the Artisan startup hook in a service provider, ideally gated to local:
use Bamarni\Composer\Autocomplete\Command\CompletionCommand;
use Illuminate\Support\Facades\Artisan;
public function boot(): void
{
if ($this->app->environment('local')) {
Artisan::starting(function ($artisan) {
$artisan->add(new CompletionCommand());
});
}
}
Crucially, complete against an executable artisan (shebang #!/usr/bin/env php, chmod +x), or an alias, because shell completion fires on the command word. php artisan <TAB> completes for php, not Artisan.
Distribute completion with your tool. For Composer-installable CLIs, ship an installer command (e.g. my-cli completion:install) that writes the generated hook to ~/.bash_completion.d/ or share/zsh/site-functions/. Package managers (Homebrew formulas) can call the same --generate-hook output at build time.
Multiple tools, one setup. With a global install and a stable Composer bin directory on PATH, the same CompletionCommand can be embedded in every in-house Symfony Console binary. Keep the generated function names unique per tool (usually derived from the program name) to avoid shell-level collisions.
Suggesting option values. The command can usually only complete what the Command definition exposes (names, shortcuts, static argument/option descriptions). For dynamic values (branches, tenant IDs, environments), implement Symfony’s Command::complete() on newer versions so the parser delegates to your callback; on older stacks, provide descriptive placeholder text on the option and accept that values are not completed.
Debugging pattern. Bypass the shell entirely and invoke the internal command directly to see raw suggestions:
./bin/my-cli _completion --shell-type=bash --input="./bin/my-cli dep"
This isolates whether a problem is in your command definitions or in the shell hook wiring.
NOASSERTION — inspect the actual LICENSE file before redistributing, and pin a version. For new projects on Symfony Console 5.4+, prefer the built-in completion command.php bin/console but the hook was generated for bin/console, or if your zsh alias expands differently from the registered name, completion silently does nothing. Regenerate with the correct program name and use the exact invocation style.opcache.enable_cli=1, avoid container booting/DB connections in the completion path, or generate static completion scripts where dynamic suggestions aren’t needed.docker execs into the container (which is slow) or generate completions on the host.autoload -U compinit && compinit. If the hook is bash-compatible rather than native zsh, you may also need autoload -U bashcompinit && bashcompinit before sourcing.composer global require installs into a bin dir that isn’t always on PATH (composer global config bin-dir --absolute). Missing that directory makes both the command and the hook unavailable._completion is an internal, underscore-prefixed command. It’s generally hidden from list, but don’t document or depend on its output format as a public API — it can change between releases.=, or quotes can confuse the parser behind --input. If a specific command line misbehaves, test it verbatim through the direct --input= debugging call rather than through the shell..bashrc hooks are not loaded in non-interactive shells (CI, some IDEs, remote SSH session wrappers) — completion failures there are usually environmental, not package bugs.Command, so you can subclass it to change the program name, hide it, or add shell-specific post-processing. For richer suggestions, hook into Symfony’s completion input/output abstractions if your Console version supports them; otherwise the package’s parser only knows command/option metadata.How can I help you explore Laravel packages today?