Search by

rmb32 / menu

rogerbarnfather

A generic, JSON-driven CLI menu engine — drives any menu-shaped flow

v1.0.0 2026-10-07 12:15 UTC

This package is auto-updated.

Last update: 2026-10-07 18:34:40 UTC


README

Part of BarnSuite.

A generic, JSON-driven CLI menu engine. Describe a multi-step flow — a wizard, a setup script, anything shaped like "ask something, do something, decide what's next" — as a JSON file of named menus, and Menu turns it into a running interaction: prompting, validating, branching, looping, and dispatching writes, all driven by data rather than hand-written control flow.

Requirements

  • PHP 8.5+

Installation

composer require rmb32/menu

The idea

A menu graph is a JSON object keyed by menu id; each entry is one menu, discriminated by a "type" field:

  • text — a free-typed prompt, stored (or dispatched) verbatim.
  • select — a choice between dynamically-fetched options, plus optional fixed "create new" and "back" branches.
  • repeat — a user-driven "add another?" loop.
  • forEach — a bounded, data-driven loop over a supplied item list.

Three things a consumer implements to plug their own domain in:

  • OptionSource — supplies a select's dynamic choices, or a forEach's items.
  • DispatchAction — the one write escape hatch; runs against a menu's own resolved data, e.g. to persist something once an answer is confirmed.
  • TextValidator — rejects a typed answer and forces a re-ask.

Everything a menu needs is declared explicitly via needs/optionalNeeds — there's no implicit access to the whole running AppState, only the keys a menu says it depends on. See docs/menu-schema.md for the full field-by-field reference.

Quick example

use Rmb32\Menu\Api\Factory\MenuEngineFactory;
use Rmb32\Menu\Api\Factory\StateMachineFactory;
use Rmb32\Menu\Api\ValueObject\AppState;
use Rmb32\Menu\Api\Factory\AskerFactory;

$appState = AppState::new();
$asker = new AskerFactory()->create($input, $output);

$transitions = new MenuEngineFactory()->create()->build(
    'path/to/menu-graph.json',
    $appState,
    $asker,
    optionSources: ['myChoices' => new MyOptionSource()],
);

$result = new StateMachineFactory()->create()->run($transitions, 'start', $appState);

$result->finished; // true if the flow reached a `{"type": "finish"}` outcome

Related packages

  • Barnspec CLI — uses Menu to drive the draft / review / refine wizards.
  • Barnspec — the application those wizards belong to.
  • Filesystem — the other shared plumbing package.

More docs

Menu schema reference · Internals · History · Known issues

License

Proprietary. See LICENSE. Copyright (c) Roger Barnfather.