Repository navigation
Releases
1.3.2
What's new since 1.3.0
- Added the OpenSSF Best Practices badge and stopped the release job waiting on Release Drafter runs for other commits. @AlexSkrypnyk (#227)
- Added OpenSSF Scorecard and Semgrep scans, scoped workflow permissions and released assets onto a draft from a pushed tag. @AlexSkrypnyk (#226)
Full Changelog: 1.3.0...1.3.2
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified - comments stripped, class body on 1 line |
Prompty.compact.php |
Compacted - minified + shortened internals (~60% smaller) |
Prompty.min.payload, Prompty.compact.payload |
The exact code embed.php writes between the markers, in each mode |
PromptyTestTrait.php |
PHPUnit helper for testing scripts that use Prompty |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
SHA256SUMS, SHA256SUMS.asc |
Checksums of the PHP files and payloads, and their GPG signature |
Prompty.intoto.jsonl |
Build provenance attestation for the PHP files, payloads and SHA256SUMS |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same
directory, then:
# Fetch the public key and check its fingerprint before importing it
# (first time only).
curl -LO \
https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc
gpg --show-keys --with-fingerprint PUBLIC_KEY.asc
gpg --import PUBLIC_KEY.asc
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMSThe expected fingerprint, and what this check does and does not prove, are in SECURITY.md.
1.3.0
What's new since 1.2.0
Breaking changes
-
[#218] Drew the failure line for a task that throws, and closed a failed flow with a 'failed' message. @AlexSkrypnyk (#220)
A task whose action throws now draws a▲failure line with the exception message, and an exception thrown mid-flow closes the tree with a redFailedbanner by default, so acatchblock that prints the message itself now shows it twice. Print only when!Prompty::isDrawn($e), or pass afailed:callable that returnsNULLto draw no banner. The newfailedargument sits beforenumberinginrun(), so passnumberingand later arguments by name if you passed them positionally. -
[#217] Added 'cancel_on_no' to 'confirm()' ahead of 'help', so a No can cancel the flow and later arguments moved 1 position. @AlexSkrypnyk (#219)
Withcancel_on_no: TRUE, a No answer (a key press, the default, a discovered value, an environment variable or non-interactive mode) draws the flow'scancelledmessage andrun()returnsNULL, the same as Escape. The new argument sits betweendescriptionandhelpin bothPrompty::confirm()andPromptyFlow::confirm(), so a call passinghelpor a later argument positionally now lands it incancel_on_no; pass those arguments by name. -
[#205] Passed the collected answers to the action of a flow 'task()'. @AlexSkrypnyk (#207)
A flow->task()now calls its action as$action($results), withNULLfor skipped steps, so the action can read what was answered without reaching forPrompty::results(). Closures with no parameters keep working, but a built-in function passed directly as the action, such as'time', now gets 1 argument and throwsArgumentCountError; wrap it in a closure. StandalonePrompty::task()still calls its action with no arguments.
Highlights
-
[#221] Drew a rejected discovered or default answer before throwing, and added 'Prompty::isDrawn()'. @AlexSkrypnyk (#224)
Adiscovered:value, an environment variable or a non-interactivedefaultthat failsrequired:orvalidate:now draws the failing step with its value and the error message before theInvalidArgumentExceptionis thrown, where before the flow closed with no explanation. The newPrompty::isDrawn($e)tells acatchblock whether Prompty already showed the exception, so a script prints only the failures that aren't on screen yet. -
[#222] Kept a wrapped line's leading spaces on its continuation rows in notes and other text bodies. @AlexSkrypnyk (#223)
When a line of a note, label, description, hint, help text, section description or task line wraps, its continuation rows now keep the line's leading spaces, the same waybox()already wrapped. An indented list such as numbered "Next steps" in a note stays together on a narrow terminal instead of the wrapped tail drifting 2 columns left. -
[#212] Fixed re-embedding adding a kill switch to a script embedded without one. @AlexSkrypnyk (#213)
Re-embedding a script that was first embedded with--no-killswitch, or that carries its own guard, used to inject aSHOULD_PROCEEDearlyreturnthat turned it into a silent no-op,--versionincluded, while the verification still passed.embed.phpnow adds the kill switch on the first embed only and prints a notice when it does, and a re-embed no longer collapses blank lines outside the markers, so everything outside them stays exactly as it was. -
[#211] Fixed a flow's 'intro', 'outro' and 'cancelled' callable drawing nothing when it returns a string. @AlexSkrypnyk (#214)
Anintro,outroorcancelledcallable can now return the message to draw, such asfn(array $r): string => $r['dish'] . ' is ready.', where before the return value was thrown away and the tree was left open. A callable returningNULLstill draws its own output, any other return type now throwsUnexpectedValueException, and a string message that happens to name a PHP function ('strtoupper') is drawn as text instead of being called. -
[#204] Accepted a closure for the message and type of a flow 'note()'. @AlexSkrypnyk (#209)
PromptyFlow::note()now takes a closure for both the message and the type, resolved against the answers collected so far when the note is reached. A "Next steps" note can mention whatever matches an earlier choice in 1->note()call, instead of 1 note per variant each behind its own->when(). -
[#206] Added 'PromptyFlow::unnumbered()' to let flow steps opt out of step numbering. @AlexSkrypnyk (#208)
Chaining->unnumbered()after a step leaves it without a(n)suffix and out of the'total'count, so a flow that asks 3 questions and then runs 2 tasks shows(1/3)instead of(1/5). Tasks stay numbered by default, so existing flows draw what they drew before, and the children of an unnumbered step walk with numbering off.
Other changes
-
Raised the dev dependency floors to the latest releases and repinned 'sticky-pull-request-comment'. @AlexSkrypnyk (#225)
Therequire-devfloors now match the latest releases (PHPUnit^12.5.44, PHPStan^2.3.5, Rector^2.8.4and 2 more), and the coverage comment action is repinned to its currentv3commit. Runtime requirements are unchanged: the library still needs only PHP 8.3 or later. -
[#203] Fixed a flow's 'outro' and 'cancelled' message drawing a second bar line after the last entry. @AlexSkrypnyk (#210)
A flow's stringoutroandcancelledmessages now sit 1 bar line below the last entry instead of 2, and so does aPrompty::outro()called from an outro callable. A standalonePrompty::outro()outside a flow keeps its leading bar. -
Removed Patreon from funding platforms. @AlexSkrypnyk (#202)
The repository's sponsor button now lists GitHub Sponsors only; nothing in the library changed.
Full Changelog: 1.2.0...1.3.0
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified - comments stripped, class body on 1 line |
Prompty.compact.php |
Compacted - minified + shortened internals (~60% smaller) |
PromptyTestTrait.php |
PHPUnit helper for testing scripts that use Prompty |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same directory, then:
# Fetch the public key and check its fingerprint before importing it
# (first time only).
curl -LO \
https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc
gpg --show-keys --with-fingerprint PUBLIC_KEY.asc
gpg --import PUBLIC_KEY.asc
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMSThe expected fingerprint, and what this check does and does not prove, are in SECURITY.md.
1.2.0
What's new since 1.1.0
Breaking changes
-
[#196] Replaced the 'flow()' steps array with a fluent 'PromptyFlow' builder, step providers and NULL answers for skipped steps. @AlexSkrypnyk (#197)
Prompty::flow()no longer takes a steps callable, and widgets no longer acceptcondition:orchildren:, so every 1.1.0 flow has to be rewritten as a chain: each array key becomes the first argument of the matching builder method (->text('dish', 'Dish name')), eachcondition:becomes a->when()after its step, eachchildren: [...]becomes->children()...->end(), and the flow options move to->run(numbering: TRUE, ...). In return, any value argument can be a closure that receives the answers so far, and->add()takes your ownPromptyStepInterfaceobjects. A step skipped by its condition now answersNULLinstead of being missing from the results, soarray_key_exists()checks need to become=== NULLchecks, whileisset()and??behave as before. -
Renamed boolean properties and variables to read as yes/no questions with 'is', 'has', 'was', 'should', 'use' and 'show' prefixes. @AlexSkrypnyk (#186)
3 protected properties that shipped in 1.1.0 have new names:$inFlowis now$isInFlow,$cfgUnicodeis now$cfgUseUnicodeand$cfgAnsiis now$cfgUseAnsi. This only affects code that subclassesPromptyor reaches in through reflection, such as a test callingpromptySetStatic('inFlow', ...), so search and replace the 3 names; public named arguments likeconfigure(unicode:, ansi:)are unchanged. Other protected helpers changed shape during this release too (walkFlow()now returns a string andreadKey()takes an argument), so re-check any subclass that overrides internals. -
[#144] Dropped PHP 8.2 support and upgraded the dev dependencies that required PHP 8.3. @AlexSkrypnyk (#147)
composer.jsonnow requires PHP 8.3 or later, so Composer won't install 1.2.0 on PHP 8.2. Projects still on 8.2 should stay on 1.1.x until they move to PHP 8.3. The PHPUnit 12 and coding-standard upgrades that came with it only affect contributors.
Highlights
-
[#164] Added a 'back_key' (default 'Ctrl+U') that steps back to the previous step in a flow. @AlexSkrypnyk (#178)
Until now an answer was final the moment you pressed Enter, so fixing a typo 3 questions back meant cancelling the whole flow. In a flow, Ctrl+U now reopens the previous step with the answer it was accepted with, and later answers come back as defaults, so stepping forward again is just Enter; steps answered fromdiscovered:or an environment variable, tasks and pauses are skipped on the way back. Change the key withback_keyonconfigure()or->run()(ctrl-atoctrl-z), or set it to''to turn it off. -
[#152] Added 'Prompty::table()' and 'Prompty::box()' renderers. @AlexSkrypnyk (#174)
Prompty::table($rows, headers: [...])andPrompty::box($content, title: '...')draw bordered tables and titled panels inside the flow tree, so an answer summary or a "what's next" panel finally matches the widgets around it. Cells wrap to the terminal width, a plain string in$rowsbecomes a section row spanning every column, and both honour ASCII mode,ansi/NO_COLORand theoutputtarget. -
[#155] Added 'Prompty::task()' with a spinner and a 'stream' option that draws the action's output under the label. @AlexSkrypnyk (#176)
Prompty::task()runs an action behind a spinner, then swaps it for a✓or▲result line with an optional hint and detail lines. Withstream: TRUEit draws everything the action prints,passthru()andsystem()output included, dim and indented under the label: a live window keeps the last 5 lines, collapses to✓ Tart baked (9 lines)on success and expands into the full log on failure. In a flow,->task('key', ...)storestrueorfalse, and without a live terminal it prints plain lines with no redraws. -
[#129] Added a 'suggest()' widget that filters options as the user types. @AlexSkrypnyk (#142)
Prompty::suggest()is a new free-text widget that lists matching options as you type, so a long list such as 400 timezones no longer has to be a giantselect(). Matching is case-insensitive by substring, ↑/↓ focus a suggestion, and Enter with nothing focused takes the typed text as is.optionscan also be a closure that receives the typed text, and the match list scrolls withscroll. -
[#125] Added a 'validate' callback to every widget that shows the error and re-prompts. @AlexSkrypnyk (#139)
Every widget takes avalidatecallback that returnsNULLfor a good answer or an error message, and on an error the widget stays open, shows the message under the input and asks again. The callback gets the answer aftertransform, plus the answers collected so far in a flow. Adiscovered:value, aPROMPTY_*variable or a non-interactive default that fails throwsInvalidArgumentExceptionbefore anything is drawn. -
[#126] Added a 'transform' callback to every widget that rewrites the answer before it is returned and drawn. @AlexSkrypnyk (#141)
Atransformcallback on every widget rewrites the answer before it's returned, and the completed line shows the transformed value, so trimming a URL down tokitchen.exampleno longer leaveshttps://www.Kitchen.Example/on screen. It runs the same way on typed input,discovered:values, environment variables and non-interactive defaults, beforerequiredandvalidate, and it must return the widget's own answer type. -
[#130] Added an 'interactive' option that answers every widget from its discovered value or default without reading input. @AlexSkrypnyk (#140)
Withinteractive: FALSEonconfigure()or->run(), every widget answers without reading a key: thediscovered:value first, then thePROMPTY_*variable, then thedefault. A script with a--no-interactionflag can now run unattended in CI instead of cancelling at the first prompt, and the completed lines still print as a log. Atext()with an empty default answers'', never its placeholder, because nobody saw it.
Other changes
-
[#200] Fixed 'configure(output:)' inside a streamed 'task()' dropping the rest of the task's output. @AlexSkrypnyk (#201)
When a streamed task's action calledPrompty::configure(output: ...), for example a nested command configuring Prompty with the stream it was handed, every later line, nested tasks included, disappeared without an error. A running task now keeps drawing to the output it started on, and the new target takes over once the task finishes. -
[#198] Fixed a streamed 'task()' nested in another dropping its result line and the outer action's later output. @AlexSkrypnyk (#199)
A streamed task run inside another streamed task's action lost its result line and everything the outer action printed after it, while the outer task still reported success. Streamed tasks now nest to any depth, each drawing indented inside its parent's output. -
Drew the seeded 'text' and 'suggest' default dim and added in-place editing with the arrow, Home, End and Delete keys. @AlexSkrypnyk (#195)
Adefaultseeded intotext()orsuggest()is now drawn dim until you edit it, so it reads as a suggestion rather than something already typed. The input also supports in-place editing with ←, →, Home, End and Delete, and the newinput_defaultandinput_cursorcolour keys restyle the default and the cursor. -
[#193] Reused the detected terminal width for 1 second instead of running 'stty size' on every frame. @AlexSkrypnyk (#194)
The terminal width that line wrapping relies on is now measured at most once a second, andstty sizeis skipped entirely when standard input isn't a terminal. Widgets in tests, CI and piped runs take 0.1-0.3 ms each instead of 15-25 ms, which adds up fast in a suite that drives 40 prompts per test. -
[#190] Regrouped the README into 5 sections with folded detail, fixed the blank widget screenshots and added 4 widget grids. @AlexSkrypnyk (#191)
The README now has a contents index and 5 main sections (Installation, Usage, Widgets, Flows and Configuration), with examples folded into expandable details and the old anchors still resolving. The widget screenshots, which rendered as a blank screen, now show their widget, and Suggest, Pause, Notes and Tasks gained their own grids. -
[#189] Aliased 'this' to a short local in '--compact' embeds. @AlexSkrypnyk (#192)
embed.php --compactnow binds$thisto a 1-character local in methods that use it often, which trims about 1 KB from the compact payload. Closures keep their own$this, and--minoutput is unchanged. -
Added a '.payload' file and size stats to embedding, stripped header tool tags and shortened parameter names in '--compact'. @AlexSkrypnyk (#187)
Embedding now writes the inserted code to a<Class>.<mode>.payloadfile beside the target script and prints aSizes:table; pass--no-payload-fileto skip the file. The embedded header drops@phpstan-*and similar tool tags, and--compactrenames parameters per method, which cut the compact payload by about 10 KB. -
Added a CI step that runs every playground script, and fixed 3 issues found by running them. @AlexSkrypnyk (#188)
CI now runs every playground demo on each PHP version and fails on a non-zero exit or any stderr output. The one library fix it turned up: a cancelled widget with no answer drew│ (cancelled)with 1 space too many, and now reads│ (cancelled). -
**[#184] Grouped the 'Prompty' methods by concern and renamed the playgro...
1.1.0
What's new since 1.0.0
- Rewrote the README, contributing guide and security policy for readability, and documented 'discovered:' precedence. @AlexSkrypnyk (#124)
- Added verifiable embeds with marker hashes, release payloads, attestations and 'embed.php --check'. @AlexSkrypnyk (#123)
- Update toshimaru/auto-author-assign action to v3.1.1 @renovate[bot] (#122)
- Rejected step keys reused anywhere in a flow tree. @AlexSkrypnyk (#121)
Full Changelog: 1.0.0...1.1.0
@AlexSkrypnyk and @renovate[bot]
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified - comments stripped, class body on 1 line |
Prompty.compact.php |
Compacted - minified + shortened internals (~60% smaller) |
PromptyTestTrait.php |
PHPUnit helper for testing scripts that use Prompty |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same
directory, then:
# Fetch the public key and check its fingerprint before importing it
# (first time only).
curl -LO \
https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc
gpg --show-keys --with-fingerprint PUBLIC_KEY.asc
gpg --import PUBLIC_KEY.asc
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMSThe expected fingerprint, and what this check does and does not prove, are in SECURITY.md.
1.0.0
What's new since 0.7.0
Breaking changes
- Changed Ctrl+C to cancel the current widget instead of ending the script. @AlexSkrypnyk (#118)
In 0.7.0, Ctrl+C on a real terminal sent SIGINT and killed the script mid-prompt, leaving the cursor hidden; now the widget draws(cancelled), restores the terminal and returnsnull, as the README already documented. A script that ignores thatnulltherefore keeps running with empty answers where it used to stop, and Ctrl+Z no longer suspends it while a prompt is open. No setting brings SIGINT back, so check everyPrompty::flow()result and standalone widget answer fornulland stop there, asstarter.phpnow does.
Highlights
-
[#117] Fixed a lone Escape press waiting for another key before cancelling a widget. @AlexSkrypnyk (#119)
On a real terminal, Escape on its own did nothing until the next key arrived and then swallowed that key; it now cancels the widget after about 50 ms. 0.7.0 also read every escape sequence except a plain arrow as Escape, so Delete, Home, End, F-keys and Shift+Tab cancelled the widget, as did arrows from a terminal that sends them as\eOA; now those arrows work as arrow keys and the rest are ignored. The 50 ms is a deliberate trade-off: if a key's bytes arrive more than 50 ms apart, over a slow SSH link say, it reads as Escape. -
Fixed typing into 'Prompty::text()' crashing on PHP builds without the 'mbstring' extension. @AlexSkrypnyk (#118)
text()calledmb_strlen()andmb_substr(), butcomposer.jsonnever requiredext-mbstring, so typing into a text prompt was a fatal error on PHP builds without it, such as a stock Ubuntuphp-cli. Prompty now calls no mbstring functions at all, and Backspace still removes a whole multi-byte character, not just its last byte. -
Fixed 'starter.php' running the real work after a cancelled flow. @AlexSkrypnyk (#118)
WithSHOULD_PROCEED=1set, a cancelled flow fell through the kill switch and ran the real work with empty answers. The template now stops whenPrompty::flow()returnsnull, and the README snippet shows the same guard. Scripts copied from an earlierstarter.phpdon't have it, so add the 3-line check by hand, which matters more now that Ctrl+C cancels a flow too. -
Fixed 'embed.php' crashing on PHP builds without the 'posix' extension. @AlexSkrypnyk (#118)
After writing the embedded script,embed.phpcalledposix_isatty()before its verification run, so on PHP builds without ext-posix, which some distributions package separately, it died with a fatal error unless--no-verifyor--no-killswitchskipped that step. It now uses the corestream_isatty(), so the verification run works without ext-posix. -
Synced the README and docblocks with the code. @AlexSkrypnyk (#118)
About 12 docblocks and several README passages described behaviour the code doesn't have, and now match it. Among the fixes,nullforunicodeoransiinconfigure()andflow()keeps the current setting rather than re-running auto-detection, and the kill switchembed.phpinjects sits ahead of your prompts, so the script stops before asking anything unlessSHOULD_PROCEEDis set. The README also lists the keys each map-valued configuration option accepts, and the verification steps below now check the signing key's fingerprint before importing it.
Other changes
-
Update shivammathur/setup-php digest to eb7c497 @renovate[bot] (#116)
-
Update all dependencies @renovate[bot] (#115)
-
Update release-drafter/release-drafter digest to 72967cd @renovate[bot] (#114)
-
Update release-drafter/release-drafter digest to c345907 @renovate[bot] (#113)
-
Update codecov/codecov-action digest to 303a32d @renovate[bot] (#112)
-
Update codecov/codecov-action digest to 0b35c9e @renovate[bot] (#111)
Full Changelog: 0.7.0...1.0.0
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified - comments stripped, class body on 1 line |
Prompty.compact.php |
Compacted - minified + shortened internals (~60% smaller) |
PromptyTestTrait.php |
PHPUnit helper for testing scripts that use Prompty |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same directory, then:
# Fetch the public key and check its fingerprint before importing it
# (first time only).
curl -LO \
https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc
gpg --show-keys --with-fingerprint PUBLIC_KEY.asc
gpg --import PUBLIC_KEY.asc
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMSThe expected fingerprint, and what this check does and does not prove, are in SECURITY.md.
0.7.0
What's new since 0.6.0
Breaking changes
-
[#97] Rejected step keys that cannot be written as an environment variable name. @AlexSkrypnyk (#107)
A step keyeddish-nameresolves toPROMPTY_DISH-NAME, whichexportrejects as a syntax error and most.envparsers will not accept, so the step could never be pre-filled and a non-interactive run simply waited on stdin.flow()now throws before the terminal is touched for any step key, children included, whose composed variable name is not a valid shell identifier, and for an empty key. Rename affected keys to letters, digits and underscores (dish-namebecomesdish_name), remembering the key is also the results-array key. -
[#96] Rejected configuration keys outside the set each argument declares. @AlexSkrypnyk (#106)
configure()merged its array arguments over the defaults witharray_replace(), so a misspelledsymbols_unicode,symbols_ascii,colors,spacingorlabelskey was accepted silently, changed nothing, and then appeared inconfig()as though it were real. Each array argument is now checked against the keys its own default declares, and an emptytruthyorfalsylist is rejected too. Fix any misspelled key - the exception names it and lists the ones that would have worked. An emptyenv_prefixstays accepted deliberately, and is now documented along with the collision risk it carries. -
[#95] Rendered values and labels as printable text. @AlexSkrypnyk (#104)
A newline in a discovered value used to break out of the tree, carriage returns and escape sequences reached the terminal intact (a\rredraws over the line,ESC[2Kerases it), andtext()handed the raw bytes back to the caller. Everything a widget draws now passes through a newprintable(), so control characters and escape sequences are removed and a newline or tab becomes a space, andtext()returns the same text it drew. Callers emitting their own colour should usePrompty::output(), which is left unsanitised as the deliberate escape hatch. -
[#94] Scoped a flow's configuration arguments to that flow. @AlexSkrypnyk (#103)
Every configuration argumentflow()accepts (unicode,ansi,env_prefix,labels,truthy,falsy,colors,symbols_unicode,symbols_ascii,spacing) wrote straight to the singleton, so a flow asking for ASCII symbols or its own prefix left that in force for every later flow in the process; a leakedenv_prefixwas the sharpest case, silently resolving later prompts from variables they never asked for. A flow's arguments now last only as long as the flow, restored whether it completes, is cancelled or throws. Move anything meant to be global intoPrompty::configure(), which still persists for the rest of the process. -
[#80] Read a comma-separated 'discovered' string the same as an environment value. @AlexSkrypnyk (#84)
multiselect()split a comma-separated string into option keys only when it arrived from an environment variable; the same string indiscovered:was treated as one literal key and rejected. A scalar from either source now splits on commas, so--extras=bread,olivesandPROMPTY_EXTRAS=bread,olivesare interchangeable. The break is narrow: an option key containing a comma is no longer reachable through the string form, sincediscovered: 'salt,pepper'is now read as two keys and rejected, so pass it as a list instead,discovered: ['salt,pepper']. -
[#81] Trimmed 'text()' discovered values and resolved an empty result to the default or placeholder. @AlexSkrypnyk (#85)
An empty answer totext()meant two things: pressing enter on an empty buffer returned the placeholder, whilediscovered: ''orPROMPTY_DISH=returned an empty string, untrimmed, whereselect()andmultiselect()both trim theirs. A discovered value is now trimmed, and an empty result resolves to the seededdefault, or theplaceholderwhen there is none - the same answer the interactive path gives. Review any step whose variable is populated by a command that can return nothing, since that now yields the placeholder rather than''. Text typed interactively is still returned exactly as typed. -
[#79] Held 'default' to the same option keys as 'discovered'. @AlexSkrypnyk (#83)
A mistypeddefaultdegraded silently:select()focused the first option as though none had been given, andmultiselect()opened with nothing checked, which reads on screen like a broken defaults feature rather than a caller error.defaultis now validated againstoptionsin the same pre-TTY block asdiscovered, and a key matching nothing throws, naming the widget, the value and the available options. Check defaults that are assembled dynamically; an empty default (''or[]) still means "no default" and stays valid. -
[#78] Rejected an empty options map in 'select()' and 'multiselect()'. @AlexSkrypnyk (#82)
An emptyoptionsmap failed differently on each path: Enter onselect()gaveUndefined array key 0then aTypeErrornaming an internal render helper, an arrow key gaveDivisionByZeroError: Modulo by zero, and the discovered path reported a confusingAvailable options: none. Both widgets now throwNo options declared for "<label>". Provide at least one option.before anything is drawn or any key is read. Two previously quiet inputs throw as well, so guard an options list built dynamically:multiselect(options: [], discovered: [])used to return[], andselect(options: [], discovered: 'x')used to report the unmatched value. -
[#72] Rejected discovered values outside a widget's declared domain. @AlexSkrypnyk (#74)
select()andmultiselect()enforced theiroptionswhitelist absolutely for a user pressing keys and not at all for a discovered value, soselect('Pick', options: ['a' => 'Alpha'], discovered: 'unknown')returnedunknown- a value the caller never offered - and the mistake surfaced later as downstream corruption. A discovered value must now be an answer the widget would have accepted interactively, or it throws naming the widget, the value and what was allowed.confirm()comes under the same rule, which also fixesdiscovered: 'no'returningTRUEfrom a bare(bool)cast. Audit any script feedingPROMPTY_*values not guaranteed to be in the list, and notemultiselect()now returns its keys deduplicated and in option order. -
[#43] Renamed the env_value context key to discovered. @AlexSkrypnyk (#55)
One value carried three names through the widget pipeline, and this settles them ondiscovered. The publicdiscovered:argument is unchanged, but theenv_valuecontext key is nowdiscovered, with no alias and no deprecation window, so a step closure reading$ctx['env_value']silently receivesNULLand falls through to whatever it does for "nothing discovered". Search step closures for$ctx['env_value']and change it to$ctx['discovered']- a one-line edit, but nothing fails loudly to prompt it. -
[#41] Stripped private-mode escape sequences in the test trait. @AlexSkrypnyk (#51)
promptyStripAnsi()used/\033\[[0-9;]*[A-Za-z]/, which cannot match a private-mode sequence because?is not in the character class - and Prompty emits exactly those,\033[?25land\033[?25h, around every interactive widget and flow. The?is now optional, so output returned bypromptyRun()andpromptyRunScript()loses cursor show/hide bytes it previously kept. Re-run consumer tests that assert on captured output; any that filtered those bytes by hand can drop the workaround. -
Renamed the protected 'cursor()' and 'flowWalk()' methods and dropped 'is_last' from the render helpers. @AlexSkrypnyk (#39)
This landed as an internal consistency pass, but it moves protected surface that subclasses and the shippedpromptyCallProtected()helper reach by name:cursor()is nowpointer()andflowWalk()is nowwalkFlow(), and the never-readbool $is_lastparameter was dropped fromrenderCompleted()andrenderCancelled(), shifting$openfrom fifth position to fourth. A newhideCursor()mirrors the existingshowCursor(). Rename those two calls, and drop theis_lastargument from any positional call to the two render helpers.
Highlights
-
[#93] Fixed step conditions evaluating against incomplete results during lookahead. @AlexSkrypnyk (#100)
walkFlow()decided a step's tree connector by evaluating later siblings' conditions before the current step's answer was stored, so a condition reading an earlier sibling's result hitUndefined array key- and under a warnings-to-exceptions handler, a common idiom in PHP tooling, aborted the whole flow with anErrorException. The decision is deferred to a newsettleOpen(), called just before each widget draws its completed line, so a condition only ever runs against answers already on record. It also corrects the tree: a step whose only visible successor depends on its own answer no longer draws itself as the last branch. -
[#91] Cancelled widgets at the end of the input stream instead of looping. @AlexSkrypnyk (#98)
At EOFreadKey()returned an empty string, which matched no branch in any widget's loop, so the widget redrew and read again forever:php script.php < /dev/nullspun at 100% CPU and never returned, and one measurement produced 89MB of discarded output in two seconds.readKey()now returnseofand a newisCancelKey()treats it likectrl-candescape, so all four widgets cancel,flow()returnsNULL, and the terminal is restored. The test trait always backs its keystroke helpers with an in-memory stream too, so a test that runs out of keystrokes cancels rather than blocking on real STDIN. -
**[#76] Normalised numeric-string option keys in...
0.6.0
What's new since 0.5.0
- Pin dependencies @renovate[bot] (#23)
- Added a cursor caret to 'select' and 'multiselect' list widgets. @AlexSkrypnyk (#26)
- Update all dependencies (major) @renovate[bot] (#24)
Full Changelog: 0.5.0...0.6.0
@AlexSkrypnyk, @renovate[bot] and renovate[bot]
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified — comments stripped, single line |
Prompty.compact.php |
Compacted — minified + shortened internals (~60% smaller) |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same
directory, then:
# Import the public key (first time only).
curl -sL https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc | gpg --import
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMS0.5.0
What's new since 0.4.0
- [#21] Added 'default' parameter to 'text', 'select', and 'multiselect' widgets. @AlexSkrypnyk (#22)
Full Changelog: 0.4.0...0.5.0
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified — comments stripped, single line |
Prompty.compact.php |
Compacted — minified + shortened internals (~60% smaller) |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same
directory, then:
# Import the public key (first time only).
curl -sL https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc | gpg --import
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMS0.4.0
What's new since 0.3.0
- Adopted 'alexskrypnyk/phpunit-helpers' for functional tests. @AlexSkrypnyk (#20)
- Improved test structure with shared helpers and functional test separation @AlexSkrypnyk (#19)
- [#12] Added '--source' flag and re-embedding support to embed.php. @AlexSkrypnyk (#18)
- [#15] Added 'PromptyTestTrait.php' to release assets and signing. @AlexSkrypnyk (#17)
- [#13] Added
version()support. @AlexSkrypnyk (#16) - Fixed embedding issues. @AlexSkrypnyk (#14)
Full Changelog: 0.3.0...0.4.0
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified — comments stripped, single line |
Prompty.compact.php |
Compacted — minified + shortened internals (~60% smaller) |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same
directory, then:
# Import the public key (first time only).
curl -sL https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc | gpg --import
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMS0.3.0
What's new since 0.2.0
- Added '--stdout' flag and release workflow with assets + signing. @AlexSkrypnyk (#11)
Full Changelog: 0.2.0...0.3.0
Release assets
| File | Description |
|---|---|
Prompty.php |
Full class with comments |
Prompty.min.php |
Minified — comments stripped, single line |
Prompty.compact.php |
Compacted — minified + shortened internals (~60% smaller) |
starter.php |
Example script template |
embed.php |
Embed/minify helper |
Verifying release assets
Download the asset files, SHA256SUMS, and SHA256SUMS.asc into the same
directory, then:
# Import the public key (first time only).
curl -sL https://raw.githubusercontent.com/AlexSkrypnyk/prompty/main/PUBLIC_KEY.asc | gpg --import
# Verify the signature.
gpg --verify SHA256SUMS.asc SHA256SUMS
# Verify file checksums.
sha256sum -c SHA256SUMS