laravel/prompts
Laravel Prompts is a PHP package for adding beautiful, user-friendly forms to command-line apps, with browser-like features including placeholder text and validation. Perfect for Artisan console commands or any CLI PHP project.
Architecture fit
laravel/prompts is a self-contained, zero-dependency-at-runtime PHP library that renders interactive terminal forms (text, textarea, number, select, multiselect, search, multisearch, suggest, confirm, spinner, progress, table, grid, note, and multi-step form() with conditional steps). Architecturally it sits at the presentation edge of any CLI surface: it consumes stdin, writes ANSI sequences to stdout, and returns typed scalars (or transformed values via transform()). It holds no state, opens no network or DB connections, and imposes no container, service-provider, or framework coupling. That makes it a low-blast-radius dependency.
Two adoption profiles matter:
laravel/framework (10.x+). Adoption cost is effectively zero — the helpers (text(), select(), confirm(), spin(), progress(), form()) are already autoloaded in Artisan commands. There is no procurement, no new vendor relationship, and no new CVE surface beyond what the framework already ships.composer require laravel/prompts with PHP 8.1+; usable with or without Symfony Console, with an adapter available for Symfony\Component\Console output.The real architectural decision is not "should we install this" but "do we standardize our CLI input layer on it." I recommend adopting it behind a thin internal wrapper rather than calling prompts directly across dozens of commands — see Integration Approach.
Integration feasibility
High. There is no data migration, no schema, no config publishing, and no bootstrapping required. A pilot can be shipped in a single sprint by converting one existing $this->ask() / $this->confirm() call site. The package provides the three things that typically block CLI UX standardization:
validate: with automatic re-prompting, replacing ad-hoc while (!valid) loops.Prompt::fallbackWhen() and Prompt::interactive() let CI, cron, and piped invocations bypass rendering and return deterministic defaults. This is the single most important capability for us and the reason I would not build this in-house.Prompts::fake() with expect()/assertSee() style assertions means prompt flows are unit-testable rather than manually verified. Existing commands with untested interactive branches become testable as a side effect of migration.Technical risk
illuminate/collections circular dependency; 0.1.x reshuffled fallback vs. non-interactive precedence). Mitigation: pin to a ~0.3.0 constraint, treat minor bumps as reviewable PRs, and keep the wrapper thin so a swap or version bump touches one file.stty, and ANSI support. The maintainers have invested heavily here (fallback when stty fails, Windows DirectWrite, NullOutput handling, multibyte/wide-character fixes, graceful failed-read handling), but exotic environments — misconfigured Docker without -it, some IDE embedded terminals, Git Bash on Windows, aggressively stripped container images — remain the residual risk. Note that the project is Laravel-maintained (Jess Archer, Joe Tannenbaum, crynobone), which materially de-risks abandonment.confirm() that silently falls through to a default in non-interactive mode can drop a database or force-push a branch. This is the one risk that requires a policy, not a technical control: destructive confirmations must never rely on a fallback default of true.Dependents: 0 is an artifact of the scraper, not reality — this package ships inside every modern Laravel install. The composite score of 39 reflects sparse repo-level signals (no long-form docs in-repo; docs live on laravel.com), not adoption risk. Weight the score accordingly and do not treat it as a negative signal.Key questions
0.x minor upgrades?Stack fit
Strong fit for Laravel-first shops with PHP 8.1+; acceptable for standalone Composer CLI tools. It is explicitly not a fit for web request paths, queue workers, or any non-TTY context where interactive input is expected — those must go through the fallback path by design.
Migration path
Adopt incrementally, never as a big-bang rewrite:
$this->ask(, $this->confirm(, $this->choice(, $this->anticipate(, and raw fgets(STDIN) across Artisan commands and installer scripts. Classify each call site as interactive-only, must-work-unattended, or destructive.App\Support\Cli\Ui) exposing only the prompt types we sanction. This centralizes theme/colors, enforces fallback defaults, and gives us a one-file blast radius for 0.x bumps.make:-style generator) and ship it. Validate on macOS, Linux, Windows PowerShell, Docker -it, and a non-TTY CI runner.--force flag so the prompt is never the sole gate.Prompts::fake() tests as each batch lands.table(), grid(), progress(), spinners).Compatibility
PHP 8.1 through 8.4 are supported (8.4 compatibility landed in 0.3.2/0.3.8). Laravel 12 support landed in 0.3.4. Symfony Console integration is available for non-Laravel contexts. The 0.3.0 removal of the illuminate/collections dependency means the package is now genuinely framework-agnostic — a meaningful plus if we ever extract CLI tooling into a standalone binary.
Sequencing
Do the wrapper and the fallback policy before the bulk rollout, not after. Retrofitting non-interactive defaults onto 40 already-converted commands is significantly more expensive than declaring them at conversion time. Estimated effort: wrapper + policy ~3–5 days; pilot ~2 days; per-command conversion is typically sub-day and is naturally absorbed into existing feature work.
Maintenance
Very low. Actively maintained by the Laravel core team with a high-cadence patch stream and a public changelog; CI covers the tests badge, PHPStan is in the toolchain, and the maintainers have demonstrated responsiveness to edge-case terminal bugs. There is no server, no daemon, no runtime footprint, and no upgrade treadmill beyond occasional 0.x minor bumps. The dominant ongoing cost is our own wrapper, which is small and intentional.
Support
Support burden shifts from "why didn't the prompt accept my input" tickets to environment-shaped tickets: terminals that mis-render, containers without a TTY, and users who pipe input. The package's error messages for unsupported environments are explicit (improved since 0.1.1), which shortens triage. Internal documentation should cover exactly two things: how to run commands non-interactively, and what to do when a terminal misbehaves.
Scaling
Non-issue horizontally — it's client-side CLI rendering. The only scaling dimension worth noting is organizational: as more commands adopt it, output formatting and prompt copy become a consistency surface. The wrapper solves this by making theme and phrasing centralized.
Failure modes (with mitigations)
| Failure mode | Impact | Mitigation |
|---|---|---|
| Non-TTY / CI run blocks on a prompt | Pipeline hangs to timeout | Mandatory fallback defaults; Prompt::fallbackWhen(); wrapper refuses prompts without a default |
Destructive confirm defaults to true unattended |
Data loss | Policy: destructive actions require explicit --force; never a silent default |
stty unavailable / restricted container |
Rendering errors or fallback mode | Package already falls back; test in our target container images |
| Wide/emoji/multibyte misalignment | Cosmetic corruption in boxes and tables | Test with representative content; package has an active fix history here |
| Piped stdin injection in automation | Unintended answers accepted | Treat piped input as non-interactive; require flags for automated runs |
0.x minor bump breaks wrapper |
Build or runtime error in CLI tooling | Pin ~0.3.0; wrapper isolates API surface; review minor bumps |
| Early exit mid-prompt leaves terminal in raw mode | Garbled shell after abort | Package handles via destructors/shutdown (since 0.1.9); verify on our abort paths |
Ramp-up
Minimal for PHP developers — the API is a set of global helper functions with named arguments (placeholder, default, hint, required, validate, transform, scroll, info), and the multi-step form() API reads declaratively. Realistic ramp: a competent Laravel developer is productive in under a day. The larger cultural lift is instilling the discipline around non-interactive defaults and fake-based tests, which is a process change rather than a learning curve.
Verdict
Adopt. Cost of entry is near zero in a Laravel estate, the maintainership is as strong as open-source gets, and the fallback + faking capabilities replace code we would otherwise hand-roll and maintain forever. The only genuine risks are policy risks — pre-1.0 pinning discipline and destructive-action gating — both of which are cheap to control up front and expensive to retrofit. Recommend proceeding with a wrapper-first, pilot-then-rollout sequence, and explicitly declining to adopt if any target command cannot define a safe unattended default.
How can I help you explore Laravel packages today?