public function run()
    {
        if (!isset($_SERVER['argv'][1])) {
            $_SERVER['argv'][1] = 'commands';
        }

        $input = $_SERVER['argv'][1];

        // Force `commands` command
        if (in_array(strtolower(trim($input)), ['-h', '--help', 'help'], true)) {
            $input = $_SERVER['argv'][1] = 'commands';
        }

        if (in_array(strtolower(trim($input)), ['--version', '-v', '--info'], true)) {
            $input = $_SERVER['argv'][1] = 'info';
        }

        // Try to dispatch the command
        foreach ($this->_commands as $command) {
            if ($command->hasIdentifier($input)) {
                return $this->dispatch($command);
            }
        }

        // Check for alternatives
        $available = [];
        foreach ($this->_commands as $command) {
            $providedCommands = $command->getCommands();
            foreach ($providedCommands as $alias) {
                $soundex = soundex($alias);
                if (!isset($available[$soundex])) {
                    $available[$soundex] = [];
                }

                $available[$soundex][] = $alias;
            }
        }

        // Show exception with/without alternatives
        $soundex = soundex($input);
        $message = sprintf('%s is not a recognized command.', $input);

        if (isset($available[$soundex])) {
            throw new ScriptException(sprintf('%s Did you mean: %s?', $message, join(' or ', $available[$soundex])));
        }

        throw new ScriptException($message);
    }