public function run(array $argv)
    {
        $this->setProgramName($argv[0]);

        $currentCommand = $this;

        // init application,
        // before parsing options, we have to known the registered commands.
        $currentCommand->init();

        // use getoption kit to parse application options
        $parser = new ContinuousOptionParser($currentCommand->getOptionCollection());

        // parse the first part options (options after script name)
        // option parser should stop before next command name.
        //
        //    $ app.php -v -d next
        //                  |
        //                  |->> parser
        //
        //
        $currentCommand->setOptions(
            $parser->parse($argv)
        );

        if (false === $currentCommand->prepare()) {
            return false;
        }


        $commandStack = [];
        $arguments = [];

        // build the command list from command line arguments
        while (!$parser->isEnd()) {
            $a = $parser->getCurrentArgument();

            // if current command is in subcommand list.
            if ($currentCommand->hasCommands()) {

                if (!$currentCommand->hasCommand($a)) {
                    if (!$this->getOption('no-interact') && ($guess = $currentCommand->guessCommand($a)) !== null) {
                        $a = $guess;
                    } else {
                        throw new CommandNotFoundException($currentCommand, $a);
                    }
                }

                $parser->advance(); // advance position

                // get command object of "$a"
                /** @var Command $nextCommand */
                $nextCommand = $currentCommand->getCommand($a);

                $parser->setSpecs($nextCommand->getOptionCollection());

                // parse the option result for command.
                $nextCommand->setOptions(
                    $parser->continueParse()
                );

                $commandStack[] = $currentCommand = $nextCommand; // save command object into the stack
            } else {
                $r = $parser->continueParse();

                if (count($r)) {
                    // get the option result and merge the new result
                    $currentCommand->getOptions()->merge($r);
                } else {
                    $a = $parser->advance();
                    $arguments[] = $a;
                }
            }
        }

        foreach ($commandStack as $cmd) {
            if (false === $cmd->prepare()) {
                return false;
            }
        }

        // get last command and run
        if ($lastCommand = array_pop($commandStack)) {

            $lastCommand->executeWrapper($arguments);
            $lastCommand->finish();
            while ($cmd = array_pop($commandStack)) {
                // call finish stage.. of every command.
                $cmd->finish();
            }

        } else {
            // no command specified.
            $this->executeWrapper($arguments);

            return true;
        }

        $currentCommand->finish();
        $this->finish();

        return true;
    }