PHPackages                             siktec/just-cli - 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. siktec/just-cli

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

siktec/just-cli
===============

Command line interface library for PHP

v2.0.1(2y ago)024MITPHPPHP &gt;=8.0

Since Jan 30Pushed 2y agoCompare

[ Source](https://github.com/siktec-lab/just-cli)[ Packagist](https://packagist.org/packages/siktec/just-cli)[ RSS](/packages/siktec-just-cli/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

Just CLI
--------

[](#just-cli)

Note

Just CLI is a fork of [adhocore/php-cli](https://github.com/adhocore/phalcon-ext/tree/master/example/cli) which is an amazing CLI framework. This fork is for adding some features that may not be suitable for the original project. Some of the features are:

- Negative numbers in arguments.
- Support for multiple variadic arguments with a grouping option (e.g. `$ app cmd [arg11 arg12] --opt [val1 val2]` )
- Add support for a `structured` CLI app that will auto register commands, views, layouts, etc.
- Add support for Themes.

Framework agnostic Command Line Interface utilities and helpers for PHP. Build Console App with ease, fun and love.

- Zero dependency.
- For PHP 8.0+ only.

[![Screen Preview](https://camo.githubusercontent.com/32e29e5f727f6570da33b0508d4209f6e65870f3e66fa4eac6dde19082b72e84/68747470733a2f2f692e696d6775722e636f6d2f71495967395a6e2e676966 "Preview from adhocore/phalcon-ext which uses this cli package")](https://camo.githubusercontent.com/32e29e5f727f6570da33b0508d4209f6e65870f3e66fa4eac6dde19082b72e84/68747470733a2f2f692e696d6775722e636f6d2f71495967395a6e2e676966)

#### What's included

[](#whats-included)

**Core:** [Argv parser](#argv-parser) · [Cli application](#console-app) · [Shell](#shell)

**IO:** [Colorizer](#color) · [Cursor manipulator](#cursor) · [Progress bar](#progress-bar) · [Stream writer](#writer) · [Stream reader](#reader)

**Other:** [Autocompletion](#autocompletion)

Installation
------------

[](#installation)

```
composer require siktec/just-Cli
```

Usage
-----

[](#usage)

### Argv parser

[](#argv-parser)

```
$command = new JCli\Input\Command('rmdir', 'Remove dirs');

$command
    ->version('0.0.1-dev')
    // Arguments are separated by space
    // Format: `` for required, `[name]` for optional
    //  `[name:default]` for default value, `[name...]` for variadic (last argument)
    ->arguments(' [dirs...]')
    // `-h --help`, `-V --version`, `-v --verbosity` options are already added by default.
    // Format: `` for required, `[name]` for optional
    ->option('-s --with-subdir', 'Also delete sub-dirs (`with` means false by default)')
    ->option('-e,--no-empty', 'Delete empty (`no` means true by default)')
    // Specify sanitizer/callback as 3rd param, default value as 4th param
    ->option('-d|--depth [nestlevel]', 'How deep to process sub-dirs', 'intval', 5)
    ->parse(['thisfile.php', '-sev', 'dir', 'dir1', 'dir2', '-vv']) // `$_SERVER['argv']`
;

// Print all values:
print_r($command->values());

/*Array
(
    [help] =>
    [version] => 0.0.1
    [verbosity] => 3
    [dir] => dir
    [dirs] => Array
        (
            [0] => dir1
            [1] => dir2
        )

    [subdir] => true
    [empty] => false
    [depth] => 5
)*/

// To get values for options except the default ones (help, version, verbosity)
print_r($command->values(false));

// Pick a value by name
$command->dir;   // dir
$command->dirs;  // [dir1, dir2]
$command->depth; // 5
```

#### Command help

[](#command-help)

It can be triggered manually with `$command->showHelp()` or automatic when `-h` or `--help` option is passed to `$command->parse()`.

For above example, the output would be: [![Command Help](https://camo.githubusercontent.com/81e445749c27848e40570e1dec2cdcb96e08c3143b4d9b0318507cda4fda980c/68747470733a2f2f692e696d6775722e636f6d2f54444151724e332e706e67 "Command Help")](https://camo.githubusercontent.com/81e445749c27848e40570e1dec2cdcb96e08c3143b4d9b0318507cda4fda980c/68747470733a2f2f692e696d6775722e636f6d2f54444151724e332e706e67)

#### Command version

[](#command-version)

It can be triggered manually with `$command->showVersion()` or automatic when `-V` or `--version` option is passed to `$command->parse()`.

For above example, the output would be:

```
0.0.1-dev

```

### Console app

[](#console-app)

Here we simulate a `git` app with limited functionality of `add`, and `checkout`. You will see how intuitive, fluent and cheese building a console app is!

#### Git app

[](#git-app)

```
$app = new JCli\Application('git', '0.0.1');

$app
    // Register `add` command
    ->command('add', 'Stage changed files', 'a') // alias a
        // Set options and arguments for this command
        ->arguments(' [paths...]')
        ->option('-f --force', 'Force add ignored file', 'boolval', false)
        ->option('-N --intent-to-add', 'Add content later but index now', 'boolval', false)
        // Handler for this command: param names should match but order can be anything :)
        ->action(function ($path, $paths, $force, $intentToAdd) {
            array_unshift($paths, $path);

            echo ($intentToAdd ? 'Intent to add ' : 'Add ')
                . implode(', ', $paths)
                . ($force ? ' with force' : '');

            // If you return integer from here, that will be taken as exit error code
        })
        // Done setting up this command for now, tap() to retreat back so we can add another command
        ->tap()
    ->command('checkout', 'Switch branches', 'co') // alias co
        ->arguments('')
        ->option('-b --new-branch', 'Create a new branch and switch to it', false)
        ->option('-f --force', 'Checkout even if index differs', 'boolval', false)
        ->action(function ($branch, $newBranch, $force) {
            echo 'Checkout to '
                . ($newBranch ? 'new ' . $branch : $branch)
                . ($force ? ' with force' : '');
        })
;

// Parse only parses input but doesn't invoke action
$app->parse(['git', 'add', 'path1', 'path2', 'path3', '-f']);

// Handle will do both parse and invoke action.
$app->handle(['git', 'add', 'path1', 'path2', 'path3', '-f']);
// Will produce: Add path1, path2, path3 with force

$app->handle(['git', 'co', '-b', 'master-2', '-f']);
// Will produce: Checkout to new master-2 with force
```

### Organized app

[](#organized-app)

Instead of inline commands/actions, we define and add our own commands (having `interact()` and `execute()`) to the app:

```
class InitCommand extends JCli\Input\Command
{
    public function __construct()
    {
        parent::__construct('init', 'Init something');

        $this
            ->argument('', 'The Arrg')
            ->argument('[arg2]', 'The Arg2')
            ->option('-a --apple', 'The Apple')
            ->option('-b --ball', 'The ball')
            // Usage examples:
            ->usage(
                // append details or explanation of given example with ` ## ` so they will be uniformly aligned when shown
                '  init --apple applet --ball ballon  ## details 1' .
                // $0 will be interpolated to actual command name
                '  $0 -a applet -b ballon  [arg2] ## details 2'
            );
    }

    // This method is auto called before `self::execute()` and receives `Interactor $io` instance
    public function interact(JCli\IO\Interactor $io) : void
    {
        // Collect missing opts/args
        if (!$this->apple) {
            $this->set('apple', $io->prompt('Enter apple'));
        }

        if (!$this->ball) {
            $this->set('ball', $io->prompt('Enter ball'));
        }

        // ...
    }

    // When app->handle() locates `init` command it automatically calls `execute()`
    // with correct $ball and $apple values
    public function execute($ball, $apple)
    {
        $io = $this->app()->io();

        $io->write('Apple ' . $apple, true);
        $io->write('Ball ' . $ball, true);

        // more codes ...

        // If you return integer from here, that will be taken as exit error code
    }
}

class OtherCommand extends JCli\Input\Command
{
    public function __construct()
    {
        parent::__construct('other', 'Other something');
    }

    public function execute()
    {
        $io = $this->app()->io();

        $io->write('Other command');

        // more codes ...

        // If you return integer from here, that will be taken as exit error code
    }
}

// Init App with name and version
$app = new JCli\Application('App', 'v0.0.1');

// Add commands with optional aliases`
$app->add(new InitCommand, 'i');
$app->add(new OtherCommand, 'o');

// Set logo
$app->logo('Ascii art logo of your app');

$app->handle($_SERVER['argv']); // if argv[1] is `i` or `init` it executes InitCommand
```

#### Grouping commands

[](#grouping-commands)

Grouped commands are listed together in commands list. Explicit grouping a command is optional. By default if a command name has a colon `:` then the part before it is taken as a group, else `*` is taken as a group.

> Example: command name `app:env` has a default group `app`, command name `appenv` has group `*`.

```
// Add grouped commands:
$app->group('Configuration', function ($app) {
    $app->add(new ConfigSetCommand);
    $app->add(new ConfigListCommand);
});

// Alternatively, set group one by one in each commands:
$app->add((new ConfigSetCommand)->inGroup('Config'));
$app->add((new ConfigListCommand)->inGroup('Config'));
...
```

#### Exception handler

[](#exception-handler)

Set a custom exception handler as callback. The callback receives exception &amp; exit code. The callback may rethrow exception or may exit the program or just log exception and do nothing else.

```
$app = new JCli\Application('App', 'v0.0.1');
$app->add(...);
$app->onException(function (Throwable $e, int $exitCode) {
    // send to sentry
    // write to logs

    // optionally, exit with exit code:
    exit($exitCode);

    // or optionally rethrow, a re-thrown exception is propagated to top layer caller.
    throw $e;
})->handle($argv);
```

#### App help

[](#app-help)

It can be triggered manually with `$app->showHelp()` or automatic when `-h` or `--help` option is passed to `$app->parse()`. **Note** If you pass something like `['app', cmd', '-h']` to `$app->parse()` it will automatically and instantly show you help of that `cmd` and not the `$app`.

For above example, the output would be: [![App Help](https://camo.githubusercontent.com/c6c725ef56e5d5cf5d656e3ba0135d76a6616f8285ff0c86f62d4c171e2544a1/68747470733a2f2f692e696d6775722e636f6d2f4e707a707353302e706e67 "App Help")](https://camo.githubusercontent.com/c6c725ef56e5d5cf5d656e3ba0135d76a6616f8285ff0c86f62d4c171e2544a1/68747470733a2f2f692e696d6775722e636f6d2f4e707a707353302e706e67)

#### App version

[](#app-version)

Same version number is passed to all attached Commands. So you can trigger version on any of the commands.

### Shell

[](#shell)

Very thin shell wrapper that provides convenience methods around `proc_open()`.

#### Basic usage

[](#basic-usage)

```
$shell = new JCli\Helper\Shell($command = 'php -v', $rawInput = null);

// Waits until proc finishes
$shell->execute($async = false); // default false

echo $shell->getOutput(); // PHP version string (often with zend/opcache info)
```

#### Advanced usage

[](#advanced-usage)

```
$shell = new JCli\Helper\Shell('php /some/long/running/script.php');

// With async flag, doesn't wait for proc to finish!
$shell->setOptions($workDir = '/home', $envVars = [])
    ->execute($async = true)
    ->isRunning(); // true

// Force stop anytime (please check php.net/proc_close)
$shell->stop(); // also closes pipes

// Force kill anytime (please check php.net/proc_terminate)
$shell->kill();
```

#### Timeout

[](#timeout)

```
$shell = new JCli\Helper\Shell('php /some/long/running/script.php');

// Wait for at most 10.5 seconds for proc to finish!
// If it doesn't complete by then, throws exception
$shell->setOptions($workDir, $envVars, $timeout = 10.5)->execute();

// And if it completes within timeout, you can access the stdout/stderr
echo $shell->getOutput();
echo $shell->getErrorOutput();
```

### Cli Interaction

[](#cli-interaction)

You can perform user interaction like printing colored output, reading user input programatically and moving the cursors around with provided `JCli\IO\Interactor`.

```
$interactor = new JCli\IO\Interactor;

// For mocking io:
$interactor = new JCli\IO\Interactor($inputPath, $outputPath);
```

#### Confirm

[](#confirm)

```
$confirm = $interactor->confirm('Are you happy?', 'n'); // Default: n (no)
$confirm // is a boolean
    ? $interactor->greenBold('You are happy :)', true)  // Output green bold text
    : $interactor->redBold('You are sad :(', true);     // Output red bold text
```

#### Single choice

[](#single-choice)

```
$fruits = ['a' => 'apple', 'b' => 'banana'];
$choice = $interactor->choice('Select a fruit', $fruits, 'b');
$interactor->greenBold("You selected: {$fruits[$choice]}", true);
```

#### Multiple choices

[](#multiple-choices)

```
$fruits  = ['a' => 'apple', 'b' => 'banana', 'c' => 'cherry'];
$choices = $interactor->choices('Select fruit(s)', $fruits, ['b', 'c']);
$choices = \array_map(function ($c) use ($fruits) { return $fruits[$c]; }, $choices);
$interactor->greenBold('You selected: ' . implode(', ', $choices), true);
```

#### Prompt free input

[](#prompt-free-input)

```
$any = $interactor->prompt('Anything', rand(1, 100)); // Random default
$interactor->greenBold("Anything is: $any", true);
```

#### Prompt with validation

[](#prompt-with-validation)

```
$nameValidator = function ($value) {
    if (\strlen($value) < 5) {
        throw new \InvalidArgumentException('Name should be at least 5 chars');
    }

    return $value;
};

// No default, Retry 5 more times
$name = $interactor->prompt('Name', null, $nameValidator, 5);
$interactor->greenBold("The name is: $name", true);
```

#### Prompt hidden

[](#prompt-hidden)

> On windows platform, it may change the fontface which can be [fixed](https://superuser.com/a/757591).

```
$passValidator = function ($pass) {
    if (\strlen($pass) < 6) {
        throw new \InvalidArgumentException('Password too short');
    }

    return $pass;
};

$pass = $interactor->promptHidden('Password', $passValidator, 2);
```

[![Interactive Preview](https://camo.githubusercontent.com/b999c34627f52626327d7c208c3da12aa381074968cdea0a3cfc01b39723d4ef/68747470733a2f2f692e696d6775722e636f6d2f7159424e6432392e676966 "Interactive Preview")](https://camo.githubusercontent.com/b999c34627f52626327d7c208c3da12aa381074968cdea0a3cfc01b39723d4ef/68747470733a2f2f692e696d6775722e636f6d2f7159424e6432392e676966)

IO Components
-------------

[](#io-components)

The interactor is composed of `JCli\Input\Reader` and `JCli\Output\Writer` while the `Writer` itself is composed of `JCli\Output\Color`. All these components can be used standalone.

### Color

[](#color)

Color looks cool!

```
$color = new JCli\Output\Color;
```

#### Simple usage

[](#simple-usage)

```
echo $color->warn('This is warning');
echo $color->info('This is info');
echo $color->error('This is error');
echo $color->comment('This is comment');
echo $color->ok('This is ok msg');
```

#### Custom style

[](#custom-style)

```
JCli\Output\Color::style('mystyle', [
    'bg' => JCli\Output\Color::CYAN,
    'fg' => JCli\Output\Color::WHITE,
    'bold' => 1, // You can experiment with 0, 1, 2, 3 ... as well
]);

echo $color->mystyle('My text');
```

### Cursor

[](#cursor)

Move cursor around, erase line up or down, clear screen.

```
$cursor = new JCli\Output\Cursor;

echo  $cursor->up(1)
    . $cursor->down(2)
    . $cursor->right(3)
    . $cursor->left(4)
    . $cursor->next(0)
    . $cursor->prev(2);
    . $cursor->eraseLine()
    . $cursor->clear()
    . $cursor->clearUp()
    . $cursor->clearDown()
    . $cursor->moveTo(5, 8); // x, y
```

### Progress Bar

[](#progress-bar)

Easily add a progress bar to your output:

```
$progress = new JCli\Output\ProgressBar(100);
for ($i = 0; $i current($i);

    // Simulate something happening
    usleep(80000);
}
```

You can also manually advance the bar:

```
$progress = new JCli\Output\ProgressBar(100);

// Do something

$progress->advance(); // Adds 1 to the current progress

// Do something

$progress->advance(10); // Adds 10 to the current progress

// Do something

$progress->advance(5, 'Still going.'); // Adds 5, displays a label
```

You can override the progress bar options to customize it to your liking:

```
$progress = new JCli\Output\ProgressBar(100);
$progress->option('pointer', '>>');
$progress->option('loader', '▩');

// You can set the progress fluently
$progress->option('pointer', '>>')->option('loader', '▩');

// You can also use an associative array to set many options in one time
$progress->option([
    'pointer' => '>>',
    'loader'  => '▩'
]);

// Available options
+------------+------------------------------+---------------+
| Option     | Description                  | Default value |
+------------+------------------------------+---------------+
| pointer    | The progress bar head symbol | >             |
| loader     | The loader symbol            | =             |
| color      | The color of progress bar    | white         |
| labelColor | The text color of the label  | white         |
+------------+------------------------------+---------------+
```

### Writer

[](#writer)

Write anything in style.

```
$writer = new JCli\Output\Writer;

// All writes are forwarded to STDOUT
// But if you specify error, then to STDERR
$writer->errorBold('This is error');
```

#### Output formatting

[](#output-formatting)

You can call methods composed of any combinations: `'', 'bold', 'bg', 'fg', 'warn', 'info', 'error', 'ok', 'comment'`... in any order (eg: `bgRedFgBlack`, `boldRed`, `greenBold`, `commentBgPurple` and so on ...)

```
$writer->bold->green->write('It is bold green');
$writer->boldGreen('It is bold green'); // Same as above
$writer->comment('This is grayish comment', true); // True indicates append EOL character.
$writer->bgPurpleBold('This is white on purple background');
```

#### Free style

[](#free-style)

Many colors with one single call: wrap text with tags `` and ``For NL/EOL just use `` or `` or ``.

Great for writing long colorful texts for example command usage info.

```
$writer->colors('This is redThis has bg Green');
```

#### Raw output

[](#raw-output)

```
$writer->raw('Enter name: ');
```

#### Tables

[](#tables)

Just pass array of assoc arrays. The keys of first array will be taken as heading. Heading is auto inflected to human readable capitalized words (ucwords).

```
$writer->table([
    ['a' => 'apple', 'b-c' => 'ball', 'c_d' => 'cat'],
    ['a' => 'applet', 'b-c' => 'bee', 'c_d' => 'cute'],
]);
```

Gives something like:

```
+--------+------+------+
| A      | B C  | C D  |
+--------+------+------+
| apple  | ball | cat  |
| applet | bee  | cute |
+--------+------+------+

```

> Designing table look and feel

Just pass 2nd param `$styles`:

```
$writer->table([
    ['a' => 'apple', 'b-c' => 'ball', 'c_d' => 'cat'],
    ['a' => 'applet', 'b-c' => 'bee', 'c_d' => 'cute'],
], [
    // for => styleName (anything that you would call in $writer instance)
    'head' => 'boldGreen', // For the table heading
    'odd'  => 'bold',      // For the odd rows (1st row is odd, then 3, 5 etc)
    'even' => 'comment',   // For the even rows (2nd row is even, then 4, 6 etc)
]);

// 'head', 'odd', 'even' are all the styles for now
// In future we may support styling a column by its name!
```

#### Reader

[](#reader)

Read and pre process user input.

```
$reader = new JCli\Input\Reader;

// No default, callback fn `ucwords()`
$reader->read(null, 'ucwords');

// Default 'abc', callback `trim()`
$reader->read('abc', 'trim');

// Read at most first 5 chars
// (if ENTER is pressed before 5 chars then further read is aborted)
$reader->read('', 'trim', 5);

// Read but don't echo back the input
$reader->readHidden($default, $callback);

// Read from piped stream (or STDIN) if available without waiting
$reader->readPiped();

// Pass in a callback for if STDIN is empty
// The callback receives $reader instance and MUST return string
$reader->readPiped(function ($reader) {
    // Wait to read a line!
    return $reader->read();

    // Wait to read multi lines (until Ctrl+D pressed)
    return $reader->readAll();
});
```

#### Exceptions

[](#exceptions)

Whenever an exception is caught by `Application::handle()`, it will show a beautiful stack trace and exit with non 0 status code.

[![Exception Preview](https://user-images.githubusercontent.com/2908547/44401057-8b350880-a577-11e8-8ca6-20508d593d98.png "Exception trace")](https://user-images.githubusercontent.com/2908547/44401057-8b350880-a577-11e8-8ca6-20508d593d98.png)

### Autocompletion

[](#autocompletion)

Any console applications that are built on top of **siktec/just-cli** can entertain autocomplete of commands and options in zsh shell with oh-my-zsh.

All you have to do is add one line to the end of `~/.oh-my-zsh/custom/plugins/ahccli/jcli.plugin.zsh`:

> `compdef _jcli `

Example: `compdef _jcli your-cli-app` for your cli app named `your-cli-app`

That is cumbersome to perform manually, here's a complete command you can copy/paste/run:

#### One time setup

[](#one-time-setup)

```
mkdir -p ~/.oh-my-zsh/custom/plugins/jcli && cd ~/.oh-my-zsh/custom/plugins/jcli

[ -f ./jcli.plugin.zsh ] || curl -sSLo ./jcli.plugin.zsh https://raw.githubusercontent.com/siktec-lab/just-cli/master/jcli.plugin.zsh

chmod 760 ./jcli.plugin.zsh && cd -
```

##### Load JCli plugin

[](#load-jcli-plugin)

> This is also one time setup.

```
# Open .zshrc
nano ~/.zshrc

# locate plugins=(... ...) and add jcli
plugins=(git ... ... jcli)

# ... then save it (Ctrl + O)
```

#### Registering app

[](#registering-app)

```
# replace appname with real name eg: phint
echo compdef _jcli appname >> ~/.oh-my-zsh/custom/plugins/jcli/jcli.plugin.zsh
```

> Of course you can add multiple apps, just change appname in above command

Then either restart the shell or source the plugin like so:

```
source ~/.oh-my-zsh/custom/plugins/jcli/jcli.plugin.zsh
```

#### Trigger autocomplete

[](#trigger-autocomplete)

```
appname             # autocompletes commands
appname subcommand  # autocompletes options for subcommand
```

### Original Authors and Contributors:

[](#original-authors-and-contributors)

- [adhocore](https://github.com/adhocore)
- [sushilgupta](https://github.com/sushilgupta)

###  Health Score

23

—

LowBetter than 27% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community15

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 85.7% 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.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

839d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/67e40f0eb9b83245cdbd5d81c2554cf10e9a5e6e43544676d71a93a1cdd6b6a2?d=identicon)[siktec-lab](/maintainers/siktec-lab)

---

Top Contributors

[![adhocore](https://avatars.githubusercontent.com/u/2908547?v=4)](https://github.com/adhocore "adhocore (389 commits)")[![shlomohass](https://avatars.githubusercontent.com/u/9121932?v=4)](https://github.com/shlomohass "shlomohass (33 commits)")[![dimtrovich](https://avatars.githubusercontent.com/u/37987162?v=4)](https://github.com/dimtrovich "dimtrovich (11 commits)")[![pattisahusiwa](https://avatars.githubusercontent.com/u/11484016?v=4)](https://github.com/pattisahusiwa "pattisahusiwa (6 commits)")[![dongasai](https://avatars.githubusercontent.com/u/16734530?v=4)](https://github.com/dongasai "dongasai (5 commits)")[![daemonu](https://avatars.githubusercontent.com/u/42321029?v=4)](https://github.com/daemonu "daemonu (3 commits)")[![mmusselman](https://avatars.githubusercontent.com/u/12834506?v=4)](https://github.com/mmusselman "mmusselman (2 commits)")[![mrmusselman1](https://avatars.githubusercontent.com/u/16480168?v=4)](https://github.com/mrmusselman1 "mrmusselman1 (1 commits)")[![dependabot-preview[bot]](https://avatars.githubusercontent.com/in/2141?v=4)](https://github.com/dependabot-preview[bot] "dependabot-preview[bot] (1 commits)")[![alexjeen](https://avatars.githubusercontent.com/u/1567497?v=4)](https://github.com/alexjeen "alexjeen (1 commits)")[![StyleCIBot](https://avatars.githubusercontent.com/u/11048387?v=4)](https://github.com/StyleCIBot "StyleCIBot (1 commits)")[![usox](https://avatars.githubusercontent.com/u/5184763?v=4)](https://github.com/usox "usox (1 commits)")

---

Tags

cliconsolecommandphp-cliphp8cli-appargv-parsercli-colorcli-actioncli-writerargument-parsercli-optionstream-outputstream-inputconsole-app

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/siktec-just-cli/health.svg)

```
[![Health](https://phpackages.com/badges/siktec-just-cli/health.svg)](https://phpackages.com/packages/siktec-just-cli)
```

###  Alternatives

[adhocore/cli

Command line interface library for PHP

3501.2M50](/packages/adhocore-cli)[helhum/typo3-console

A reliable and powerful command line interface for TYPO3 CMS

2939.0M192](/packages/helhum-typo3-console)[aplus/cli

Aplus Framework CLI Library

2301.7M6](/packages/aplus-cli)[laminas/laminas-cli

Command-line interface for Laminas projects

563.7M54](/packages/laminas-laminas-cli)[inhere/console

php console library, provide console argument parse, console controller/command run, color style, user interactive, information show.

3477.4k12](/packages/inhere-console)

PHPackages © 2026

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