PHPackages                             sugarcraft/candy-shell - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [CLI &amp; Console](/categories/cli)
4. /
5. sugarcraft/candy-shell

ActiveLibrary[CLI &amp; Console](/categories/cli)

sugarcraft/candy-shell
======================

PHP port of charmbracelet/gum — composer-installable CLI of SugarCraft TUI primitives. 13 subcommands: choose, confirm, file, filter, format, input, join, log, pager, spin, style, table, write.

10PHP

Since Jun 30Pushed 1mo agoCompare

[ Source](https://github.com/sugarcraft/candy-shell)[ Packagist](https://packagist.org/packages/sugarcraft/candy-shell)[ RSS](/packages/sugarcraft-candy-shell/feed)WikiDiscussions master Synced 3w ago

READMEChangelogDependenciesVersions (1)Used By (0)

[![candy-shell](.assets/icon.png)](.assets/icon.png)

CandyShell
==========

[](#candyshell)

[![CI](https://github.com/detain/sugarcraft/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/detain/sugarcraft/actions/workflows/ci.yml)[![codecov](https://camo.githubusercontent.com/4428e8a30315663b072a2cfab18bb22d2796a0149c6aec2625f91739e6611ae8/68747470733a2f2f636f6465636f762e696f2f67682f64657461696e2f737567617263726166742f6272616e63682f6d61737465722f67726170682f62616467652e7376673f666c61673d63616e64792d7368656c6c)](https://app.codecov.io/gh/detain/sugarcraft?flags%5B0%5D=candy-shell)[![Packagist Version](https://camo.githubusercontent.com/bb7d9cc07764b42b2b0cbbfd17b94b0d642d0c0229ea9e448a24c8ea7709d187/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f737567617263726166742f63616e64792d7368656c6c3f6c6162656c3d7061636b6167697374)](https://packagist.org/packages/sugarcraft/candy-shell)[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)[![PHP](https://camo.githubusercontent.com/fd3accad83cd317a9843432403191b4e555c55d57d63e10897f3f1dff5ed169e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d254532253839254135382e332d3838393262662e737667)](https://www.php.net/)

[![demo](.vhs/style.gif)](.vhs/style.gif)

```
composer require sugarcraft/candy-shell
```

PHP port of [charmbracelet/gum](https://github.com/charmbracelet/gum) — a composer-installable CLI of SugarCraft TUI primitives, useful for shell scripts.

```
# 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"
```

Subcommands
-----------

[](#subcommands)

All 13 gum subcommands ship. Run `candyshell  --help` for the full flag list per command.

Auto-discovery
--------------

[](#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()`.

### Help attributes

[](#help-attributes)

Two additional attributes enrich the `--help` output of your commands:

- **`#[Alias('name')]`** — registers an alternative name for the command (e.g. `choose` → `cho`). Multiple aliases are supported via repeated attributes.
- **`#[Example('usage', 'description')]`** — adds an example line to the command's help block. The `description` parameter is optional. Multiple examples are supported via repeated attributes.

The `HelpFormatter` class renders these automatically when `--help` is invoked. It reads `#[Alias]` and `#[Example]` attributes via `ReflectionClass::getAttributes()` and formats them alongside the standard Symfony description and help text.

### Typo suggestion

[](#typo-suggestion)

When a user types an unknown command name, `Application::find()` runs it through a `TypoSuggester` that computes Levenshtein distance against all registered command names. If a match is found within distance ≤ 2, the error message suggests the nearest alternative (e.g. "Did you mean `choose`?"). Beyond distance 2 the original error propagates silently.

```
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();
```

`Application::scan($namespace)` iterates all already-loaded classes under that namespace, picks those bearing `#[Command]`, and registers them into the application. Classes must be autoloaded (or `require`d) before `scan()` can find them — the scanner uses `get_declared_classes()`and does not trigger autoloading.

CommandRole`choose`Pick one or many items from a list.`completion`Emit shell completion script (bash · zsh · fish).`confirm`Yes/no prompt — exit `0` on confirm, `1` on cancel.`file`Interactive file picker.`filter`Fuzzy filter over stdin lines (single- or multi-select).`format`Render Markdown / code / template / emoji to the terminal.`input`Read a single line; supports `--password` masking.`join`Concatenate two styled fragments side-by-side or stacked.`log`Levelled / structured log output (text · json · logfmt).`pager`Scrollable viewer for long input.`spin`Run an external command behind a spinner.`style`Apply Sprinkles styling to argv (or stdin).`table`Render a CSV / TSV table.`write`Multi-line text editor.Flag reference (selected highlights)
------------------------------------

[](#flag-reference-selected-highlights)

The audit lists upstream-gum flags that are not yet wired in CandyShell. The shipped surface today covers the 80 % case for shell scripts; see [AUDIT\_2026\_05\_06.md](../AUDIT_2026_05_06.md) for the full delta. Common flags across commands:

- `--limit N` / `--no-limit` / `--ordered` / `--selected="a,b"` — multi-select on `choose` and `filter`.
- `--header "Email:"` / `--prompt "> "` / `--value "$LAST"` / `--char-limit N` / `--width N` / `--max-lines N` / `--show-line-numbers`on `input` / `write`.
- `--affirmative "Yes"` / `--negative "No"` / `--default=yes|no` / `--show-output` on `confirm`.
- `--show-output` / `--show-error` plus 12 spinner styles (`dot` · `line` · `pulse` · `globe` · `points` · `monkey` · `moon`· `meter` · `mini-dot` · `hamburger` · `ellipsis` · `jump`) on `spin`.
- `--min-level info` / `--prefix` / `--time RFC3339` / `--file out.log`/ `--formatter text|json|logfmt` / `--structured` on `log`.
- `--border` / `--border-foreground "#ff0"` / `--height N` / `--trim`on `style`.

Environment
-----------

[](#environment)

CandyShell respects standard CLI colour conventions:

- **`NO_COLOR=1`** disables every SGR escape — output is plain ASCII.
- **`CLICOLOR=0`** disables colour when stdout is not a TTY (otherwise defaults to colour).
- **`CLICOLOR_FORCE=1`** keeps colour even when stdout is piped or redirected.
- **`FORCE_COLOR=1|2|3`** forces a specific tier (16 / 256 / TrueColor).

### `CANDYSHELL_*` env var fallbacks

[](#candyshell_-env-var-fallbacks)

Any command option can be given via an environment variable using the `CANDYSHELL_` prefix followed by the option name in uppercase with non-alphanumeric characters replaced by `_`. For example:

```
# Equivalent to: candyshell style --foreground=#ff0000 --bold "Hello"
CANDYSHELL_FOREGROUND=#ff0000 CANDYSHELL_BOLD=1 candyshell style "Hello"
```

The fallback is applied when no explicit CLI option is provided. An explicit flag on the command line always takes precedence over the env var.

### Shell completion

[](#shell-completion)

```
candyshell completion --shell=bash
candyshell completion --shell=zsh
candyshell completion --shell=fish
```

Emit a shell completion script for bash, zsh, or fish. Source the output directly or drop it into the appropriate completion directory.

### Version

[](#version)

`candyshell --version` reports the version read from the monorepo root `composer.json` via `Application::versionFromComposer()`. The version is discovered by walking up from the package directory to find the nearest `composer.json` with a non-empty `version` field.

Exit codes
----------

[](#exit-codes)

- `0` — normal completion. For `confirm`, this means the user picked the affirmative answer.
- `1` — `confirm` declined; or non-zero exit forwarded from the external command run by `spin`.
- `130` — interrupted (Ctrl-C / SIGINT). Matches POSIX shell convention.

Porting from gum
----------------

[](#porting-from-gum)

Most `gum X` invocations work as `candyshell X` verbatim. Known behavioural differences (also see [AUDIT\_2026\_05\_06.md](../AUDIT_2026_05_06.md)):

- `format` accepts `-t/--type` (`markdown`, `code`, `template`, `emoji`) alongside `--theme`. Template support is the lightweight `{{VAR}}`expansion — Go template-function helpers are not implemented.
- `--style` flags using the gum dotted form (`--header.foreground`, `--cursor.foreground`, …) are not yet wired across every command.
- `--timeout`, `--show-help`, `--strip-ansi`, and the `--cursor-mode`flags now accept their gum-equivalent values on every command. Where a flag is meaningless to a non-interactive command (`format`, `join`, `log`, `style`, `table`) it is still accepted for parity but treated as a no-op.
- `confirm --default=yes|no` is the form to use; the older `--default-yes`alias is preserved.

Theming and customization
-------------------------

[](#theming-and-customization)

Almost every interactive subcommand accepts a `--style` flag in `.=` form. Repeat the flag to layer properties or target multiple elements:

```
candyshell choose --style "cursor.foreground=212" \
                  --style "selected.bold=true" \
                  --style "header.foreground=99" \
                  --header "Pick a colour" red green blue
```

Available properties on every element: `foreground`, `background`, `bold`, `italic`, `underline`, `strikethrough`, `faint`, `blink`, `reverse`. Element names are documented inline in each subcommand's `--help` output (`choose` exposes `cursor`, `header`, `selected`, `unselected`; `confirm` exposes `prompt`, `selected`, `unselected`; `input`/`write` expose `prompt`, `placeholder`, `cursor`, `header`, `lineNumber`).

Colour values accept the same surface as [CandySprinkles](../candy-sprinkles/README.md): hex (`#ff8800`), ANSI 0–15 (`9` for bright red), 8-bit (`212`), and named CSS colours (`coral`, `slategray`).

Themes for `format` ride on [CandyShine](../candy-shine/README.md): pass `--theme dracula`, `--theme tokyo-night`, `--theme dark`, `--theme light`, `--theme pink`, `--theme ascii`, or `--theme notty` to swap renderer presets without authoring a Style yourself. `--type code --language=go` reuses the markdown pipeline for syntax-only rendering, while `--type emoji`expands the built-in `:smile:` shortcode set (unknown shortcodes pass through verbatim).

The same Style rules apply to the `style` subcommand, which is the canonical way to compose lipgloss-style boxes from a script:

```
candyshell style --foreground=212 --bold --border rounded \
                 --padding "1 4" --margin "0 2" "Welcome aboard"
```

Test
----

[](#test)

```
cd candy-shell && composer install && vendor/bin/phpunit
```

Demos
-----

[](#demos)

### choose

[](#choose)

[![choose](.vhs/choose.gif)](.vhs/choose.gif)

### Confirm

[](#confirm)

[![confirm](.vhs/confirm.gif)](.vhs/confirm.gif)

### file

[](#file)

[![file](.vhs/file.gif)](.vhs/file.gif)

### filter

[](#filter)

[![filter](.vhs/filter.gif)](.vhs/filter.gif)

### format

[](#format)

[![format](.vhs/format.gif)](.vhs/format.gif)

### Input

[](#input)

[![input](.vhs/input.gif)](.vhs/input.gif)

### join

[](#join)

[![join](.vhs/join.gif)](.vhs/join.gif)

### log

[](#log)

[![log](.vhs/log.gif)](.vhs/log.gif)

### pager

[](#pager)

[![pager](.vhs/pager.gif)](.vhs/pager.gif)

### spin

[](#spin)

[![spin](.vhs/spin.gif)](.vhs/spin.gif)

### Style

[](#style)

[![style](.vhs/style.gif)](.vhs/style.gif)

### Table

[](#table)

[![table](.vhs/table.gif)](.vhs/table.gif)

### write

[](#write)

[![write](.vhs/write.gif)](.vhs/write.gif)

Shared foundations
------------------

[](#shared-foundations)

The `filter` command uses [candy-fuzzy](../candy-fuzzy/README.md) for fuzzy-matching via `FilterModel` → `SmithWatermanMatcher`. The shell-style flag parser (`SubStyleParser`) operates on style-flag strings in isolation and has no fuzzy logic.

Related
-------

[](#related)

- [SugarCraft monorepo](https://github.com/detain/sugarcraft)
- Upstream: [charmbracelet/gum](https://github.com/charmbracelet/gum)

###  Health Score

20

—

LowBetter than 12% of packages

Maintenance59

Moderate activity, may be stable

Popularity2

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 99.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/b1036e0717211b8030b83cbe729e8ba6ba442fdbd5285fb97a39d7dcfe339342?d=identicon)[detain](/maintainers/detain)

---

Top Contributors

[![detain](https://avatars.githubusercontent.com/u/1364504?v=4)](https://github.com/detain "detain (131 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (1 commits)")

---

Tags

bashcandycorechoosecli-toolkitcommand-lineconfirmfiltergumgum-portinteractive-clijoinlogposixscriptingshell-toolsspinnerstyletableterminalwrite

### Embed Badge

![Health badge](/badges/sugarcraft-candy-shell/health.svg)

```
[![Health](https://phpackages.com/badges/sugarcraft-candy-shell/health.svg)](https://phpackages.com/packages/sugarcraft-candy-shell)
```

###  Alternatives

[illuminate/console

The Illuminate Console package.

13046.0M6.8k](/packages/illuminate-console)[styleci/cli

The CLI tool for StyleCI

71470.5k9](/packages/styleci-cli)[winbox/args

Windows command-line formatter

20720.9k21](/packages/winbox-args)[mallardduck/laravel-traits

A collection of useful Laravel snippets in the form of easy to use traits.

136.2k2](/packages/mallardduck-laravel-traits)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
