Laravel Prompts 0.3.15 added stream(), task(), autocomplete(), notify(), and title(). These APIs make an Artisan command behave less like a silent script and more like an interface with timing, feedback, and recovery cues.
The streaming API renders incremental output and fits AI responses that arrive token by token. The task API keeps a live log under a spinner and leaves a persistent success, warning, or error state. Autocomplete can suggest paths, configuration keys, or package names, while a selection can expose contextual information. Native notifications cover long background work.
The management consequence is practical. Internal tools fail adoption when users cannot tell whether they are working, what they expect, or what happened after a failure. A polished command does not need a full web application; it needs clear input, progress, and a result someone can act on. Laravel Prompts lowers the implementation cost of those basics in PHP.
Internal command adoption depends on visible feedback
An internal tool competes with the time of someone in an incident or an onboarding. If the command stays mute for two minutes, the person kills the process, opens another path, or pings a colleague. The package now offers the minimum a terminal product needs: suggested input, progress with a log, a result that stays on screen, and a window title while the job runs.
Joe Tannenbaum described the origin: several primitives came out of the Cloud CLI and landed in Prompts. That matters because the API is no longer only pretty inputs. It covers the cycle of a long operation. task() gets a logger for lines, stable success, warning, or error messages, and a label that can change mid-run. title() updates the tab. callout() packs a summary with a list and metadata. People who actually operate read that; they do not read a wall of text at the end.
Measure adoption with the team's own numbers, not a feeling. How many times the command ran this week. How many runs ended in error. How many people still paste the runbook into Slack. If the chart does not move after a new interface, the problem was not the spinner. It was permission, environment, or a step the command does not cover.
Do not build a TUI for an operation that should be a CI job. An interactive wizard serves someone choosing an option. A pipeline serves repetition and audit. Both can call the same PHP function. The shell around it changes.
TTY streams and CI logs need separate paths
stream() exists for text that arrives over time: model tokens, API logs, output from a child process. On a human TTY, wrapping, ANSI, and fade-in help reading. In GitHub Actions, the same flow needs a stable line, no cursor control, and an artifact another system can parse.
Prompts documents fallbacks for unsupported environments and the PCNTL extension to animate the spinner. Without PCNTL, the task looks static. That is acceptable on a laptop. On an ephemeral runner, assume the fallback and test both. An interactive prompt in CI is a design failure: Cloud CLI already disables prompts when there is no TTY. An internal command should do the same, with --no-interaction or an explicit check.
Agent streaming is also a data surface. Token-by-token output can include a slice of .env, PII from a dump, or a key the model repeated. Define what may go to the terminal and what belongs only in a redacted log. stream() does not make that distinction. The product you wrap around it must.
autocomplete() with a closure that hits disk or a database on every key is great on a laptop and expensive if the source is a slow API. Cache the list. Bound the set. The select info panel should show what a person needs to avoid picking the wrong environment: name, region, whether it is production. Package pricing is a docs illustration; on an internal command, the critical metadata is impact.
A pretty spinner does not prove the process is safe
Deploy, migrate, and seed remain operations with effects. A live bar lowers anxiety. It does not lower the chance of running migrate in production without a backup, or of an Artisan agent applying a diff without review. Prompts improves the shell. Who may fire the command, in which environment, with which confirmation, still lives in code and access control.
Use confirm() with a negative default on any destructive action. Use the info panel to repeat the target ("production, eu-west, cluster X"). Write an execution id into structured logs, the same id a callout can show in the footer. If the team only has the spinner, the postmortem becomes a screenshot.
The package ships tests for prompts. Treat the command as a product: cover the happy path, cancellation, and the no-TTY fallback. A diagnostics command that was only clicked on one person's Mac will fail on the first Windows machine without WSL or the first scheduled job.
A system notification does not close the incident
notify() on macOS can carry a subtitle and a sound; on Linux, an icon. It serves someone who started a seed and switched tabs. It does not replace an operations channel, a ticket, or the persistent state in the terminal. The person who was not at that laptop does not hear a local notification.
Split the audiences. Whoever waits at the desk gets notify and title. Whoever needs to know later gets a log line, a non-zero exit, and, if the command deploys, the same signal the pipeline already uses. Mixing the two creates the illusion that "the team was told."
Prompts lowered the cost of a CLI someone might actually use. The leadership decision is which operations deserve that shell, which should stay mute in CI, and what evidence remains when the spinner is gone. Without that sort, the team gets an LLM chat in Artisan and keeps the 400-line bash script for the work that actually touches production.
If your company is weighing something similar, reach me on LinkedIn or WhatsApp.