← All libraries

CandyShell

🐚 CandyShell

Composer-installable CLI of TUI primitives

port of gum cli shell-script one-shot

13 subcommands — choose, confirm, file, filter, format, input, join, log, pager, spin, style, table, write. Drop into a shell script and instantly get a Charm-quality interactive UX.

Install

composer require sugarcraft/candy-shell

Quickstart

# Apply styling.
candyshell style --foreground "#ff5f87" --bold "Hello, candy!"

# Pick one item.
choice=$(candyshell choose Pizza Burger Salad)

# Read a single line.
name=$(candyshell input --placeholder "Your name?")

# Confirm a destructive action.
candyshell confirm "Really delete $file?" && rm "$file"

# Show a spinner while running a command.
candyshell spin --title "Building..." -- make build

# Fuzzy-filter a list.
git branch | candyshell filter

What's in the box

styleApply Sprinkles styling to argv (or stdin) and print.
choosePick one item from a list; prints the selection to stdout.
confirmYes/no — exit code 0 on yes, 1 on no.
filterFuzzy-filter over stdin lines.
input / writeSingle-line / multi-line text input.
fileFile picker — print the selected path.
formatRender Markdown via CandyShine.
pagerScroll long input through a SugarBits Viewport.
spinShow a spinner while running an external command.
tableRender a CSV / TSV table with rounded borders.
logLeveled logging output (debug / info / warn / error / fatal).
joinConcatenate styled strings horizontally / vertically.

Auto-discovery

CandyShell uses PHP attributes to auto-discover commands at runtime. Mark any class extending Symfony\Component\Console\Command\Command with #[Command] and it will be picked up by Application::scan():

use SugarCraft\Shell\Attribute\Command;
use SugarCraft\Shell\Attribute\Flag;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[Command(name: 'mycmd', description: 'Does something useful.', descriptionSection: 'Longer help text.')]
final class MyCommand extends Command
{
    #[Flag(name: 'format', short: 'f', description: 'Output format.', enum: FormatType::class)]
    protected function configure(): void
    {
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $format = $input->getOption('format');
        // ...
        return self::SUCCESS;
    }
}

enum FormatType: string
{
    case Json = 'json';
    case Yaml = 'yaml';
}

Register the namespace in your bootstrap:

$app = new \SugarCraft\Shell\Application();
$app->scan('My\\Namespace\\');  // discovers all #[Command] classes
$app->run();

New attribute types (step 06.05)

TypeNamespaceRole
CommandSugarCraft\Shell\Attribute\Command#[Attribute] for auto-registering commands
FlagSugarCraft\Shell\Attribute\Flag#[Attribute] for declarative CLI options
ValueEnumSugarCraft\Shell\Attribute\ValueEnumConstrained enum for flag values
CommandScannerSugarCraft\Shell\Discovery\CommandScannerNamespace scanner for #[Command] classes
Application::scan()SugarCraft\Shell\ApplicationOpt-in discovery method

Enhanced help (step 06.06)

TypeNamespaceRole
AliasSugarCraft\Shell\Attribute\Alias#[Attribute] registering CLI command aliases
ExampleSugarCraft\Shell\Attribute\Example#[Attribute] adding example usage to help output
HelpFormatterSugarCraft\Shell\Help\HelpFormatterRenders enhanced help from #[Alias]/#[Example] attributes
TypoSuggesterSugarCraft\Shell\Help\TypoSuggesterLevenshtein distance ≤ 2 typo suggestions for unknown commands

Shell completions + version (step 06.07)

TypeNamespaceRole
BashCompletionSugarCraft\Shell\Completion\BashCompletionBash completion script generator
ZshCompletionSugarCraft\Shell\Completion\ZshCompletionZsh completion script generator
FishCompletionSugarCraft\Shell\Completion\FishCompletionFish completion script generator
CompletionCommandSugarCraft\Shell\CompletionCommandBuilt-in completion [--shell bash|zsh|fish] subcommand
Application::versionFromComposer()SugarCraft\Shell\ApplicationReads version from root composer.json

CandyShell also respects CANDYSHELL_* env var fallbacks — prefix any option name with CANDYSHELL_ in uppercase to set it when no explicit CLI flag is provided.

Source & demos

Try the quickstart →

CLI Commands

CommandDescription
completionEmit shell completion script (bash · zsh · fish)
styleApply Sprinkles styling to text
choosePick one item from a list
confirmYes/no confirmation
filterFuzzy-filter stdin lines
inputRead single-line text input
writeRead multi-line text input
fileFile picker
formatRender Markdown
pagerScroll long content
spinShow spinner during command
tableRender CSV/TSV table
logLeveled logging
joinJoin styled strings

Demos.

VHS-recorded GIFs of every example shipped with the library. Regenerated automatically on every push that touches the source.

choose

choose

Pick one item from a list.
confirm

confirm

Yes/no with exit-code semantics.
input

input

Read a single line.
write

write

Multi-line text editor.
file

file

Pick a file path.
filter

filter

Fuzzy filter over stdin.
format

format

Render Markdown via CandyShine.
style

style

Apply foreground / bold to argv.
table

table

Render CSV with rounded borders.
pager

pager

Scroll long input.
log

log

Leveled logging output.
spin

spin

Spinner while running a command.
join

join

Concatenate styled chunks.