diff --git a/app/console/cake b/app/console/cake
new file mode 100755
index 000000000..d4d067d59
--- /dev/null
+++ b/app/console/cake
@@ -0,0 +1,26 @@
+#!/bin/bash
+################################################################################
+#
+# Bake is a shell script for running CakePHP bake script
+# PHP 5
+#
+# CakePHP(tm) :  Rapid Development Framework (http://cakephp.org)
+# Copyright 2005-2010, Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+#
+# @copyright		Copyright 2005-2010, Cake Software Foundation, Inc.
+# @link				http://cakephp.org CakePHP(tm) Project
+# @package			cake
+# @subpackage		cake.app.console
+# @since			CakePHP(tm) v 2.0
+# @license			MIT License (http://www.opensource.org/licenses/mit-license.php)
+#
+################################################################################
+LIB=${0/%cake/}
+APP=`pwd`
+
+exec php -q ${LIB}cake.php -working "${APP}" "$@"
+
+exit;
\ No newline at end of file
diff --git a/app/console/cake.bat b/app/console/cake.bat
new file mode 100644
index 000000000..6499c2f22
--- /dev/null
+++ b/app/console/cake.bat
@@ -0,0 +1,33 @@
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Bake is a shell script for running CakePHP bake script
+:: PHP 5
+::
+:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+:: Copyright 2005-2010, Cake Software Foundation, Inc.
+::
+:: Licensed under The MIT License
+:: Redistributions of files must retain the above copyright notice.
+::
+:: @copyright		Copyright 2005-2010, Cake Software Foundation, Inc.
+:: @link				http://cakephp.org CakePHP(tm) Project
+:: @package			cake
+:: @subpackage		cake.app.console
+:: @since			CakePHP(tm) v 2.0
+:: @license			MIT License (http://www.opensource.org/licenses/mit-license.php)
+::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+:: In order for this script to work as intended, the cake\console\ folder must be in your PATH
+
+@echo.
+@echo off
+
+SET app=%0
+SET lib=%~dp0
+
+php -q "%lib%cake.php" -working "%CD%" %*
+
+echo.
+
+exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/app/console/cake.php b/app/console/cake.php
new file mode 100755
index 000000000..af02ae4ff
--- /dev/null
+++ b/app/console/cake.php
@@ -0,0 +1,25 @@
+#!/usr/bin/php -q
+<?php
+/**
+ * Command-line code generation utility to automate programmer chores.
+ *
+ * Shell dispatcher class
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+require_once(dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console' . DIRECTORY_SEPARATOR . 'shell_dispatcher.php');
+
+return ShellDispatcher::run($argv);
diff --git a/cake/console/templates/skel/tests/groups/empty b/app/console/shells/empty
similarity index 100%
rename from cake/console/templates/skel/tests/groups/empty
rename to app/console/shells/empty
diff --git a/cake/console/templates/skel/vendors/shells/tasks/empty b/app/console/shells/tasks/empty
similarity index 100%
rename from cake/console/templates/skel/vendors/shells/tasks/empty
rename to app/console/shells/tasks/empty
diff --git a/cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks/empty b/app/console/shells/templates/empty
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks/empty
rename to app/console/shells/templates/empty
diff --git a/cake/basics.php b/cake/basics.php
index 88ea14bdb..602bec763 100644
--- a/cake/basics.php
+++ b/cake/basics.php
@@ -92,20 +92,39 @@
  */
 	function debug($var = false, $showHtml = false, $showFrom = true) {
 		if (Configure::read('debug') > 0) {
+			$file = '';
+			$line = '';
 			if ($showFrom) {
 				$calledFrom = debug_backtrace();
-				echo '<strong>' . substr(str_replace(ROOT, '', $calledFrom[0]['file']), 1) . '</strong>';
-				echo ' (line <strong>' . $calledFrom[0]['line'] . '</strong>)';
+				$file = substr(str_replace(ROOT, '', $calledFrom[0]['file']), 1);
+				$line = $calledFrom[0]['line'];
 			}
-			echo "\n<pre class=\"cake-debug\">\n";
+			$html = <<<HTML
+<strong>%s</strong> (line <strong>%s</strong>)
+<pre class="cake-debug">
+%s
+</pre>
+HTML;
+			$text = <<<TEXT
 
+%s (line %s)
+########## DEBUG ##########
+%s
+###########################
+
+TEXT;
+			$template = $html;
+			if (php_sapi_name() == 'cli') {
+				$template = $text;
+			}
 			$var = print_r($var, true);
 			if ($showHtml) {
 				$var = str_replace('<', '&lt;', str_replace('>', '&gt;', $var));
 			}
-			echo $var . "\n</pre>\n";
+			printf($template, $file, $line, $var);
 		}
 	}
+
 if (!function_exists('sortByKey')) {
 
 /**
diff --git a/cake/console/cake.php b/cake/console/cake.php
index a445bb64a..2db99284e 100644
--- a/cake/console/cake.php
+++ b/cake/console/cake.php
@@ -20,638 +20,6 @@
  * @since         CakePHP(tm) v 1.2.0.5012
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
+require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR. 'shell_dispatcher.php');
 
-/**
- * Shell dispatcher
- *
- * @package       cake
- * @subpackage    cake.cake.console
- */
-class ShellDispatcher {
-
-/**
- * Standard input stream.
- *
- * @var filehandle
- * @access public
- */
-	public $stdin;
-
-/**
- * Standard output stream.
- *
- * @var filehandle
- * @access public
- */
-	public $stdout;
-
-/**
- * Standard error stream.
- *
- * @var filehandle
- * @access public
- */
-	public $stderr;
-
-/**
- * Contains command switches parsed from the command line.
- *
- * @var array
- * @access public
- */
-	public $params = array();
-
-/**
- * Contains arguments parsed from the command line.
- *
- * @var array
- * @access public
- */
-	public $args = array();
-
-/**
- * The file name of the shell that was invoked.
- *
- * @var string
- * @access public
- */
-	public $shell = null;
-
-/**
- * The class name of the shell that was invoked.
- *
- * @var string
- * @access public
- */
-	public $shellClass = null;
-
-/**
- * The command called if public methods are available.
- *
- * @var string
- * @access public
- */
-	public $shellCommand = null;
-
-/**
- * The path locations of shells.
- *
- * @var array
- * @access public
- */
-	public $shellPaths = array();
-
-/**
- * The path to the current shell location.
- *
- * @var string
- * @access public
- */
-	public $shellPath = null;
-
-/**
- * The name of the shell in camelized.
- *
- * @var string
- * @access public
- */
-	public $shellName = null;
-
-/**
- * TaskCollection object for the command
- *
- * @var TaskCollection
- */
-	protected $_Tasks;
-
-/**
- * Constructor
- *
- * The execution of the script is stopped after dispatching the request with
- * a status code of either 0 or 1 according to the result of the dispatch.
- *
- * @param array $args the argv
- * @return void
- */
-	public function __construct($args = array()) {
-		set_time_limit(0);
-
-		$this->__initConstants();
-		$this->parseParams($args);
-		$this->_initEnvironment();
-		$this->__buildPaths();
-		$this->_stop($this->dispatch() === false ? 1 : 0);
-	}
-
-/**
- * Defines core configuration.
- *
- * @access private
- */
-	function __initConstants() {
-		if (function_exists('ini_set')) {
-			ini_set('display_errors', '1');
-			ini_set('error_reporting', E_ALL & ~E_DEPRECATED);
-			ini_set('html_errors', false);
-			ini_set('implicit_flush', true);
-			ini_set('max_execution_time', 0);
-		}
-
-		if (!defined('CAKE_CORE_INCLUDE_PATH')) {
-			define('DS', DIRECTORY_SEPARATOR);
-			define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__))));
-			define('DISABLE_DEFAULT_ERROR_HANDLING', false);
-			define('CAKEPHP_SHELL', true);
-			if (!defined('CORE_PATH')) {
-				if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ini_get('include_path'))) {
-					define('CORE_PATH', null);
-				} else {
-					define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
-				}
-			}
-		}
-	}
-
-/**
- * Defines current working environment.
- *
- */
-	protected function _initEnvironment() {
-		$this->stdin = fopen('php://stdin', 'r');
-		$this->stdout = fopen('php://stdout', 'w');
-		$this->stderr = fopen('php://stderr', 'w');
-
-		if (!$this->__bootstrap()) {
-			$this->stderr("\nCakePHP Console: ");
-			$this->stderr("\nUnable to load Cake core:");
-			$this->stderr("\tMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH);
-			$this->_stop();
-		}
-
-		if (!isset($this->args[0]) || !isset($this->params['working'])) {
-			$this->stderr("\nCakePHP Console: ");
-			$this->stderr('This file has been loaded incorrectly and cannot continue.');
-			$this->stderr('Please make sure that ' . DIRECTORY_SEPARATOR . 'cake' . DIRECTORY_SEPARATOR . 'console is in your system path,');
-			$this->stderr('and check the manual for the correct usage of this command.');
-			$this->stderr('(http://manual.cakephp.org/)');
-			$this->_stop();
-		}
-
-		if (basename(__FILE__) !=  basename($this->args[0])) {
-			$this->stderr("\nCakePHP Console: ");
-			$this->stderr('Warning: the dispatcher may have been loaded incorrectly, which could lead to unexpected results...');
-			if ($this->getInput('Continue anyway?', array('y', 'n'), 'y') == 'n') {
-				$this->_stop();
-			}
-		}
-
-		$this->shiftArgs();
-	}
-
-/**
- * Builds the shell paths.
- *
- * @access private
- * @return void
- */
-	function __buildPaths() {
-		$paths = array();
-		if (!class_exists('Folder')) {
-			require LIBS . 'folder.php';
-		}
-		$plugins = App::objects('plugin', null, false);
-		foreach ((array)$plugins as $plugin) {
-			$pluginPath = App::pluginPath($plugin);
-			$path = $pluginPath . 'vendors' . DS . 'shells' . DS;
-			if (file_exists($path)) {
-				$paths[] = $path;
-			}
-		}
-
-		$vendorPaths = array_values(App::path('vendors'));
-		foreach ($vendorPaths as $vendorPath) {
-			$path = rtrim($vendorPath, DS) . DS . 'shells' . DS;
-			if (file_exists($path)) {
-				$paths[] = $path;
-			}
-		}
-
-		$this->shellPaths = array_values(array_unique(array_merge($paths, App::path('shells'))));
-	}
-
-/**
- * Initializes the environment and loads the Cake core.
- *
- * @return boolean Success.
- * @access private
- */
-	function __bootstrap() {
-
-		define('ROOT', $this->params['root']);
-		define('APP_DIR', $this->params['app']);
-		define('APP_PATH', $this->params['working'] . DS);
-		define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS);
-		if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) {
-			define('TMP', CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'tmp' . DS);
-		}
-
-		$boot = file_exists(ROOT . DS . APP_DIR . DS . 'config' . DS . 'bootstrap.php');
-		require CORE_PATH . 'cake' . DS . 'bootstrap.php';
-		require_once CORE_PATH . 'cake' . DS . 'console' . DS . 'console_error_handler.php';
-		set_exception_handler(array('ConsoleErrorHandler', 'handleException'));
-
-		if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) {
-			include_once CORE_PATH . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php';
-			App::build();
-		}
-		if (!defined('FULL_BASE_URL')) {
-			define('FULL_BASE_URL', '/');
-		}
-
-		return true;
-	}
-
-/**
- * Clear the console
- *
- * @return void
- */
-	public function clear() {
-		if (empty($this->params['noclear'])) {
-			if ( DS === '/') {
-				passthru('clear');
-			} else {
-				passthru('cls');
-			}
-		}
-	}
-
-/**
- * Dispatches a CLI request
- *
- * @return boolean
- */
-	public function dispatch() {
-		$arg = $this->shiftArgs();
-
-		if (!$arg) {
-			$this->help();
-			return false;
-		}
-		if ($arg == 'help') {
-			$this->help();
-			return true;
-		}
-		
-		list($plugin, $shell) = pluginSplit($arg);
-		$this->shell = $shell;
-		$this->shellName = Inflector::camelize($shell);
-		$this->shellClass = $this->shellName . 'Shell';
-
-		$arg = null;
-
-		if (isset($this->args[0])) {
-			$arg = $this->args[0];
-			$this->shellCommand = Inflector::variable($arg);
-		}
-
-		$Shell = $this->_getShell($plugin);
-
-		if (!$Shell) {
-			$title = sprintf(__('Error: Class %s could not be loaded.'), $this->shellClass);
-			$this->stderr($title . "\n");
-			return false;
-		}
-
-		$methods = array();
-
-		if (is_a($Shell, 'Shell')) {
-			$Shell->initialize();
-			$Shell->loadTasks();
-
-			foreach ($Shell->taskNames as $task) {
-				if (is_a($Shell->{$task}, 'Shell')) {
-					$Shell->{$task}->initialize();
-					$Shell->{$task}->loadTasks();
-				}
-			}
-
-			$task = Inflector::camelize($arg);
-
-			if (in_array($task, $Shell->taskNames)) {
-				$this->shiftArgs();
-				$Shell->{$task}->startup();
-
-				if (isset($this->args[0]) && $this->args[0] == 'help') {
-					if (method_exists($Shell->{$task}, 'help')) {
-						$Shell->{$task}->help();
-					} else {
-						$this->help();
-					}
-					return true;
-				}
-				return $Shell->{$task}->execute();
-			}
-			$methods = array_diff(get_class_methods('Shell'), array('help'));
-		}
-		$methods = array_diff(get_class_methods($Shell), $methods);
-		$added = in_array(strtolower($arg), array_map('strtolower', $methods));
-		$private = $arg[0] == '_' && method_exists($Shell, $arg);
-
-		if (!$private) {
-			if ($added) {
-				$this->shiftArgs();
-				$Shell->startup();
-				return $Shell->{$arg}();
-			}
-			if (method_exists($Shell, 'main')) {
-				$Shell->startup();
-				return $Shell->main();
-			}
-		}
-
-		$title = sprintf(__('Error: Unknown %1$s command %2$s.'), $this->shellName, $arg);
-		$message = sprintf(__('For usage try `cake %s help`'), $this->shell);
-		$this->stderr($title . "\n" . $message . "\n");
-		return false;
-	}
-
-/**
- * Get shell to use, either plugin shell or application shell
- *
- * All paths in the shellPaths property are searched.
- * shell, shellPath and shellClass properties are taken into account.
- *
- * @param string $plugin Optionally the name of a plugin
- * @return mixed False if no shell could be found or an object on success
- */
-	protected function _getShell($plugin = null) {
-		foreach ($this->shellPaths as $path) {
-			$this->shellPath = $path . $this->shell . '.php';
-			$pluginShellPath =  DS . $plugin . DS . 'vendors' . DS . 'shells' . DS;
-
-			if ((strpos($path, $pluginShellPath) !== false || !$plugin) && file_exists($this->shellPath)) {
-				$loaded = true;
-				break;
-			}
-		}
-		if (!isset($loaded)) {
-			return false;
-		}
-
-		if (!class_exists('Shell')) {
-			require CONSOLE_LIBS . 'shell.php';
-		}
-
-		if (!class_exists($this->shellClass)) {
-			require $this->shellPath;
-		}
-		if (!class_exists($this->shellClass)) {
-			return false;
-		}
-		$Shell = new $this->shellClass($this);
-		return $Shell;
-	}
-
-/**
- * Returns a TaskCollection object for Shells to use when loading their tasks.
- *
- * @return TaskCollection object.
- */
-	public function getTaskCollection() {
-		if (empty($this->_Tasks)) {
-			$this->_Tasks = new TaskCollection($this);
-		}
-		return $this->_Tasks;
-	}
-
-/**
- * Prompts the user for input, and returns it.
- *
- * @param string $prompt Prompt text.
- * @param mixed $options Array or string of options.
- * @param string $default Default input value.
- * @return Either the default value, or the user-provided input.
- */
-	public function getInput($prompt, $options = null, $default = null) {
-		if (!is_array($options)) {
-			$printOptions = '';
-		} else {
-			$printOptions = '(' . implode('/', $options) . ')';
-		}
-
-		if ($default === null) {
-			$this->stdout($prompt . " $printOptions \n" . '> ', false);
-		} else {
-			$this->stdout($prompt . " $printOptions \n" . "[$default] > ", false);
-		}
-		$result = fgets($this->stdin);
-
-		if ($result === false) {
-			exit;
-		}
-		$result = trim($result);
-
-		if ($default != null && empty($result)) {
-			return $default;
-		}
-		return $result;
-	}
-
-/**
- * Outputs to the stdout filehandle.
- *
- * @param string $string String to output.
- * @param boolean $newline If true, the outputs gets an added newline.
- * @return integer Returns the number of bytes output to stdout.
- */
-	public function stdout($string, $newline = true) {
-		if ($newline) {
-			return fwrite($this->stdout, $string . "\n");
-		} else {
-			return fwrite($this->stdout, $string);
-		}
-	}
-
-/**
- * Outputs to the stderr filehandle.
- *
- * @param string $string Error text to output.
- */
-	public function stderr($string) {
-		fwrite($this->stderr, $string);
-	}
-
-/**
- * Parses command line options
- *
- * @param array $params Parameters to parse
- */
-	public function parseParams($params) {
-		$this->__parseParams($params);
-		$defaults = array('app' => 'app', 'root' => dirname(dirname(dirname(__FILE__))), 'working' => null, 'webroot' => 'webroot');
-		$params = array_merge($defaults, array_intersect_key($this->params, $defaults));
-		$isWin = false;
-		foreach ($defaults as $default => $value) {
-			if (strpos($params[$default], '\\') !== false) {
-				$isWin = true;
-				break;
-			}
-		}
-		$params = str_replace('\\', '/', $params);
-
-		if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) {
-			if (empty($this->params['app']) && $params['working'] != $params['root']) {
-				$params['root'] = dirname($params['working']);
-				$params['app'] = basename($params['working']);
-			} else {
-				$params['root'] = $params['working'];
-			}
-		}
-
-		if ($params['app'][0] == '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) {
-			$params['root'] = dirname($params['app']);
-		} elseif (strpos($params['app'], '/')) {
-			$params['root'] .= '/' . dirname($params['app']);
-		}
-
-		$params['app'] = basename($params['app']);
-		$params['working'] = rtrim($params['root'], '/') . '/' . $params['app'];
-
-		if (!empty($matches[0]) || !empty($isWin)) {
-			$params = str_replace('/', '\\', $params);
-		}
-
-		$this->params = array_merge($this->params, $params);
-	}
-
-/**
- * Helper for recursively parsing params
- *
- * @return array params
- * @access private
- */
-	function __parseParams($params) {
-		$count = count($params);
-		for ($i = 0; $i < $count; $i++) {
-			if (isset($params[$i])) {
-				if ($params[$i]{0} === '-') {
-					$key = substr($params[$i], 1);
-					$this->params[$key] = true;
-					unset($params[$i]);
-					if (isset($params[++$i])) {
-						if ($params[$i]{0} !== '-') {
-							$this->params[$key] = str_replace('"', '', $params[$i]);
-							unset($params[$i]);
-						} else {
-							$i--;
-							$this->__parseParams($params);
-						}
-					}
-				} else {
-					$this->args[] = $params[$i];
-					unset($params[$i]);
-				}
-
-			}
-		}
-	}
-
-/**
- * Removes first argument and shifts other arguments up
- *
- * @return mixed Null if there are no arguments otherwise the shifted argument
- */
-	public function shiftArgs() {
-		return array_shift($this->args);
-	}
-
-/**
- * Shows console help
- *
- */
-	public function help() {
-		$this->clear();
-		$this->stdout("\nWelcome to CakePHP v" . Configure::version() . " Console");
-		$this->stdout("---------------------------------------------------------------");
-		$this->stdout("Current Paths:");
-		$this->stdout(" -app: ". $this->params['app']);
-		$this->stdout(" -working: " . rtrim($this->params['working'], DS));
-		$this->stdout(" -root: " . rtrim($this->params['root'], DS));
-		$this->stdout(" -core: " . rtrim(CORE_PATH, DS));
-		$this->stdout("");
-		$this->stdout("Changing Paths:");
-		$this->stdout("your working path should be the same as your application path");
-		$this->stdout("to change your path use the '-app' param.");
-		$this->stdout("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp");
-
-		$this->stdout("\nAvailable Shells:");
-		$shellList = array();
-		foreach ($this->shellPaths as $path) {
-			if (!is_dir($path)) {
-				continue;
-			}
- 			$shells = App::objects('file', $path);
-			if (empty($shells)) {
-				continue;
-			}
-			if (preg_match('@plugins[\\\/]([^\\\/]*)@', $path, $matches)) {
-				$type = Inflector::camelize($matches[1]);
-			} elseif (preg_match('@([^\\\/]*)[\\\/]vendors[\\\/]@', $path, $matches)) {
-				$type = $matches[1];
-			} elseif (strpos($path, CAKE_CORE_INCLUDE_PATH . DS . 'cake') === 0) {
-				$type = 'CORE';
-			} else {
-				$type = 'app';
-			}
-			foreach ($shells as $shell) {
-				if ($shell !== 'shell.php') {
-					$shell = str_replace('.php', '', $shell);
-					$shellList[$shell][$type] = $type;
-				}
-			}
-		}
-		if ($shellList) {
-			ksort($shellList);
-			if (DS === '/') {
-				$width = exec('tput cols') - 2;
-			}
-			if (empty($width)) {
-				$width = 80;
-			}
-			$columns = max(1, floor($width / 30));
-			$rows = ceil(count($shellList) / $columns);
-
-			foreach ($shellList as $shell => $types) {
-				sort($types);
-				$shellList[$shell] = str_pad($shell . ' [' . implode ($types, ', ') . ']', $width / $columns);
-			}
-			$out = array_chunk($shellList, $rows);
-			for ($i = 0; $i < $rows; $i++) {
-				$row = '';
-				for ($j = 0; $j < $columns; $j++) {
-					if (!isset($out[$j][$i])) {
-						continue;
- 					}
-					$row .= $out[$j][$i];
- 				}
-				$this->stdout(" " . $row);
-			}
-		}
-		$this->stdout("\nTo run a command, type 'cake shell_name [args]'");
-		$this->stdout("To get help on a specific command, type 'cake shell_name help'");
-	}
-
-/**
- * Stop execution of the current script
- *
- * @param $status see http://php.net/exit for values
- * @return void
- */
-	protected function _stop($status = 0) {
-		exit($status);
-	}
-}
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	$dispatcher = new ShellDispatcher($argv);
-}
+return ShellDispatcher::run($argv);
diff --git a/cake/console/console_error_handler.php b/cake/console/libs/console_error_handler.php
similarity index 86%
rename from cake/console/console_error_handler.php
rename to cake/console/libs/console_error_handler.php
index 10a8028fd..5dda75a3c 100644
--- a/cake/console/console_error_handler.php
+++ b/cake/console/libs/console_error_handler.php
@@ -14,11 +14,11 @@
  * @link          http://cakephp.org CakePHP(tm) Project
  * @package       cake
  * @subpackage    cake.cake.console
- * @since         CakePHP(tm) v 1.2.0.5074
+ * @since         CakePHP(tm) v 2.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-
 App::import('Core', 'ErrorHandler');
+require_once 'console_output.php';
 
 /**
  * Error Handler for Cake console. Does simple printing of the 
@@ -44,7 +44,7 @@ class ConsoleErrorHandler extends ErrorHandler {
  * @param array $messages Error messages
  */
 	function __construct($error) {
-		$this->stderr = fopen('php://stderr', 'w');
+		$this->stderr = new ConsoleOutput('php://stderr');
 		parent::__construct($error);
 	}
 
@@ -105,15 +105,11 @@ class ConsoleErrorHandler extends ErrorHandler {
  * @return void
  */
 	public function _outputMessage($template = null) {
-		$this->stderr($this->error->getMessage() . "\n" . $this->error->getTraceAsString());
+		$this->stderr->write(sprintf(
+			__("<error>Error:</error> %s\n%s"), 
+			$this->error->getMessage(), 
+			$this->error->getTraceAsString()
+		));
 	}
 
-/**
- * Outputs to the stderr filehandle.
- *
- * @param string $string Error text to output.
- */
-	public function stderr($string) {
-		fwrite($this->stderr, "Error: ". $string . "\n");
-	}
 }
diff --git a/cake/console/libs/console_input.php b/cake/console/libs/console_input.php
new file mode 100644
index 000000000..33127ae9b
--- /dev/null
+++ b/cake/console/libs/console_input.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * ConsoleInput file.
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * Object wrapper for interacting with stdin 
+ *
+ * @package cake.console
+ */
+class ConsoleInput {
+/**
+ * Input value.
+ *
+ * @var resource
+ */
+	protected $_input;
+
+/**
+ * Constructor
+ *
+ * @return void
+ */
+	public function __construct($handle = 'php://stdin') {
+		$this->_input = fopen($handle, 'r');
+	}
+
+/**
+ * Read a value from the stream
+ *
+ * @return mixed The value of the stream
+ */
+	public function read() {
+		return fgets($this->_input);
+	}
+}
\ No newline at end of file
diff --git a/cake/console/libs/console_input_argument.php b/cake/console/libs/console_input_argument.php
new file mode 100644
index 000000000..3e8d1eb7d
--- /dev/null
+++ b/cake/console/libs/console_input_argument.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * ConsoleArgumentOption file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * An object to represent a single argument used in the command line.
+ * ConsoleOptionParser creates these when you use addArgument()
+ *
+ * @see ConsoleOptionParser::addArgument()
+ * @package cake.console
+ */
+class ConsoleInputArgument {
+
+	protected $_name, $_help, $_required, $_choices;
+
+/**
+ * Make a new Input Argument
+ *
+ * @param mixed $name The long name of the option, or an array with all the properites.
+ * @param string $help The help text for this option
+ * @param boolean $required Whether this argument is required. Missing required args will trigger exceptions
+ * @param arraty $choices Valid choices for this option.
+ * @return void
+ */
+	public function __construct($name, $help = '', $required = false, $choices = array()) {
+		if (is_array($name) && isset($name['name'])) {
+			foreach ($name as $key => $value) {
+				$this->{'_' . $key} = $value;
+			}
+		} else {
+			$this->_name = $name;
+			$this->_help = $help;
+			$this->_required = $required;
+			$this->_choices = $choices;
+		}
+	}
+
+/**
+ * Get the name of the argument
+ *
+ * @return string
+ */
+	public function name() {
+		return $this->_name;
+	}
+
+/**
+ * Generate the help for this this argument.
+ *
+ * @param int $width The width to make the name of the option.
+ * @return string 
+ */
+	public function help($width = 0) {
+		$name = $this->_name;
+		if (strlen($name) < $width) {
+			$name = str_pad($name, $width, ' ');
+		}
+		$optional = '';
+		if (!$this->isRequired()) {
+			$optional = __(' <comment>(optional)</comment>');
+		}
+		if (!empty($this->_choices)) {
+			$optional .= sprintf(__(' <comment>(choices: %s)</comment>'), implode('|', $this->_choices));
+		}
+		return sprintf('%s%s%s', $name, $this->_help, $optional);
+	}
+
+/**
+ * Get the usage value for this argument
+ *
+ * @return string
+ */
+	public function usage() {
+		$name = $this->_name;
+		if (!empty($this->_choices)) {
+			$name = implode('|', $this->_choices);
+		}
+		$name = '<' . $name . '>';
+		if (!$this->isRequired()) {
+			$name = '[' . $name . ']';
+		}
+		return $name;
+	}
+
+/**
+ * Check if this argument is a required argument
+ *
+ * @return boolean
+ */
+	public function isRequired() {
+		return (bool) $this->_required;
+	}
+
+/**
+ * Check that $value is a valid choice for this argument.
+ *
+ * @return boolean
+ */
+	public function validChoice($value) {
+		if (empty($this->_choices)) {
+			return true;
+		}
+		if (!in_array($value, $this->_choices)) {
+			throw new InvalidArgumentException(sprintf(
+				__('"%s" is not a valid value for %s.  Please use one of "%s"'), 
+				$value, $this->_name, implode(', ', $this->_choices)
+			));
+		}
+		return true;
+	}
+
+/**
+ * Append this argument to the passed in SimpleXml object.
+ *
+ * @param SimpleXmlElement The parent element.
+ * @return SimpleXmlElement The parent with this argument appended.
+ */
+	public function xml(SimpleXmlElement $parent) {
+		$option = $parent->addChild('argument');
+		$option->addAttribute('name', $this->_name);
+		$option->addAttribute('help', $this->_help);
+		$option->addAttribute('required', $this->isRequired());
+		$choices = $option->addChild('choices');
+		foreach ($this->_choices as $valid) {
+			$choices->addChild('choice', $valid);
+		}
+		return $parent;
+	}
+}
\ No newline at end of file
diff --git a/cake/console/libs/console_input_option.php b/cake/console/libs/console_input_option.php
new file mode 100644
index 000000000..173e19cdc
--- /dev/null
+++ b/cake/console/libs/console_input_option.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * ConsoleInputOption file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * An object to represent a single option used in the command line.
+ * ConsoleOptionParser creates these when you use addOption()
+ *
+ * @see ConsoleOptionParser::addOption()
+ * @package cake.console
+ */
+class ConsoleInputOption {
+
+	protected $_name, $_short, $_help, $_boolean, $_default, $_choices;
+
+/**
+ * Make a new Input Option
+ *
+ * @param mixed $name The long name of the option, or an array with all the properites.
+ * @param string $short The short alias for this option
+ * @param string $help The help text for this option
+ * @param boolean $boolean Whether this option is a boolean option.  Boolean options don't consume extra tokens
+ * @param string $default The default value for this option.
+ * @param arraty $choices Valid choices for this option.
+ * @return void
+ */
+	public function __construct($name, $short = null, $help = '', $boolean = false, $default = '', $choices = array()) {
+		if (is_array($name) && isset($name['name'])) {
+			foreach ($name as $key => $value) {
+				$this->{'_' . $key} = $value;
+			}
+		} else {
+			$this->_name = $name;
+			$this->_short = $short;
+			$this->_help = $help;
+			$this->_boolean = $boolean;
+			$this->_default = $default;
+			$this->_choices = $choices;
+		}
+	}
+
+/**
+ * Get the name of the argument
+ *
+ * @return string
+ */
+	public function name() {
+		return $this->_name;
+	}
+
+/**
+ * Generate the help for this this option.
+ *
+ * @param int $width The width to make the name of the option.
+ * @return string 
+ */
+	public function help($width = 0) {
+		$default = $short = '';
+		if (!empty($this->_default) && $this->_default !== true) {
+			$default = sprintf(__(' <comment>(default: %s)</comment>'), $this->_default);
+		}
+		if (!empty($this->_choices)) {
+			$default .= sprintf(__(' <comment>(choices: %s)</comment>'), implode('|', $this->_choices));
+		}
+		if (!empty($this->_short)) {
+			$short = ', -' . $this->_short;
+		}
+		$name = sprintf('--%s%s', $this->_name, $short);
+		if (strlen($name) < $width) {
+			$name = str_pad($name, $width, ' ');
+		}
+		return sprintf('%s%s%s', $name, $this->_help, $default);
+	}
+
+/**
+ * Get the usage value for this option
+ *
+ * @return string
+ */
+	public function usage() {
+		$name = empty($this->_short) ? '--' . $this->_name : '-' . $this->_short;
+		$default = '';
+		if (!empty($this->_default) && $this->_default !== true) {
+			$default = ' ' . $this->_default;
+		}
+		if (!empty($this->_choices)) {
+			$default = ' ' . implode('|', $this->_choices);
+		}
+		return sprintf('[%s%s]', $name, $default);
+	}
+
+/**
+ * Get the default value for this option
+ *
+ * @return void
+ */
+	public function defaultValue() {
+		return $this->_default;
+	}
+
+/**
+ * Check if this option is a boolean option
+ *
+ * @return boolean
+ */
+	public function isBoolean() {
+		return (bool) $this->_boolean;
+	}
+
+/**
+ * Check that a value is a valid choice for this option.
+ *
+ * @return boolean
+ */
+	public function validChoice($value) {
+		if (empty($this->_choices)) {
+			return true;
+		}
+		if (!in_array($value, $this->_choices)) {
+			throw new InvalidArgumentException(sprintf(
+				__('"%s" is not a valid value for --%s. Please use one of "%s"'), 
+				$value, $this->_name, implode(', ', $this->_choices)
+			));
+		}
+		return true;
+	}
+
+/**
+ * Append the option's xml into the parent.
+ *
+ * @param SimpleXmlElement The parent element.
+ * @return SimpleXmlElement The parent with this option appended.
+ */
+	public function xml(SimpleXmlElement $parent) {
+		$option = $parent->addChild('option');
+		$option->addAttribute('name', '--' . $this->_name);
+		$short = '';
+		if (strlen($this->_short)) {
+			$short = $this->_short;
+		}
+		$option->addAttribute('short', '-' . $short);
+		$option->addAttribute('boolean', $this->_boolean);
+		$option->addChild('default', $this->_default);
+		$choices = $option->addChild('choices');
+		foreach ($this->_choices as $valid) {
+			$choices->addChild('choice', $valid);
+		}
+		return $parent;
+	}
+}
diff --git a/cake/console/libs/console_input_subcommand.php b/cake/console/libs/console_input_subcommand.php
new file mode 100644
index 000000000..514c5a1b9
--- /dev/null
+++ b/cake/console/libs/console_input_subcommand.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * ConsoleInputSubcommand file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * An object to represent a single subcommand used in the command line.
+ * ConsoleOptionParser creates these when you use addSubcommand()
+ *
+ * @see ConsoleOptionParser::addSubcommand()
+ * @package cake.console
+ */
+class ConsoleInputSubcommand {
+
+	protected $_name;
+	protected $_help;
+	protected $_parser;
+
+/**
+ * Make a new Subcommand
+ *
+ * @param mixed $name The long name of the subcommand, or an array with all the properites.
+ * @param string $help The help text for this option
+ * @param ConsoleOptionParser $parser A parser for this subcommand.
+ * @return void
+ */
+	public function __construct($name, $help = '', $parser = null) {
+		if (is_array($name) && isset($name['name'])) {
+			foreach ($name as $key => $value) {
+				$this->{'_' . $key} = $value;
+			}
+		} else {
+			$this->_name = $name;
+			$this->_help = $help;
+			$this->_parser = $parser;
+		}
+		if (is_array($this->_parser)) {
+			$this->_parser['command'] = $this->_name;
+			$this->_parser = ConsoleOptionParser::buildFromArray($this->_parser);
+		}
+	}
+
+/**
+ * Get the name of the subcommand
+ *
+ * @return string
+ */
+	public function name() {
+		return $this->_name;
+	}
+
+/**
+ * Generate the help for this this subcommand.
+ *
+ * @param int $width The width to make the name of the subcommand.
+ * @return string 
+ */
+	public function help($width = 0) {
+		$name = $this->_name;
+		if (strlen($name) < $width) {
+			$name = str_pad($name, $width, ' ');
+		}
+		return $name . $this->_help;
+	}
+
+/**
+ * Get the usage value for this option
+ *
+ * @return mixed Either false or a ConsoleOptionParser
+ */
+	public function parser() {
+		if ($this->_parser instanceof ConsoleOptionParser) {
+			return $this->_parser;
+		}
+		return false;
+	}
+
+/**
+ * Append this subcommand to the Parent element
+ *
+ * @param SimpleXmlElement The parent element.
+ * @return SimpleXmlElement The parent with this subcommand appended.
+ */
+	public function xml(SimpleXmlElement $parent) {
+		$command = $parent->addChild('command');
+		$command->addAttribute('name', $this->_name);
+		$command->addAttribute('help', $this->_help);
+		return $parent;
+	}
+}
diff --git a/cake/console/libs/console_option_parser.php b/cake/console/libs/console_option_parser.php
new file mode 100644
index 000000000..c1779d828
--- /dev/null
+++ b/cake/console/libs/console_option_parser.php
@@ -0,0 +1,591 @@
+<?php
+/**
+ * ConsoleOptionParser file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+require_once CONSOLE_LIBS . 'console_input_option.php';
+require_once CONSOLE_LIBS . 'console_input_argument.php';
+require_once CONSOLE_LIBS . 'console_input_subcommand.php';
+require_once CONSOLE_LIBS . 'help_formatter.php';
+
+/**
+ * Handles parsing the ARGV in the command line and provides support
+ * for GetOpt compatible option definition.  Provides a builder pattern implementation
+ * for creating shell option parsers.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console
+ */
+class ConsoleOptionParser {
+
+/**
+ * Description text - displays before options when help is generated
+ *
+ * @see ConsoleOptionParser::description()
+ * @var string
+ */
+	protected $_description = null;
+
+/**
+ * Epilog text - displays after options when help is generated
+ *
+ * @see ConsoleOptionParser::epilog()
+ * @var string
+ */
+	protected $_epilog = null;
+
+/**
+ * Option definitions.
+ *
+ * @see ConsoleOptionParser::addOption()
+ * @var array
+ */
+	protected $_options = array();
+
+/**
+ * Map of short -> long options, generated when using addOption()
+ *
+ * @var string
+ */
+	protected $_shortOptions = array();
+
+/**
+ * Positional argument definitions.
+ *
+ * @see ConsoleOptionParser::addArgument()
+ * @var array
+ */
+	protected $_args = array();
+
+/**
+ * Subcommands for this Shell.
+ *
+ * @var array
+ */
+	protected $_subcommands = array();
+
+/**
+ * Construct an OptionParser so you can define its behavior
+ *
+ * ### Options
+ *
+ * Named arguments come in two forms, long and short. Long arguments are preceeded
+ * by two - and give a more verbose option name. i.e. `--version`. Short arguments are
+ * preceeded by one - and are only one character long.  They usually match with a long option,
+ * and provide a more terse alternative.
+ *
+ * #### Using Options
+ *
+ * Options can be defined with both long and short forms.  By using `$parser->addOption()`
+ * you can define new options.  The name of the option is used as its long form, and you
+ * can supply an additional short form, with the `short` option.
+ *
+ * Calling options can be done using syntax similar to most *nix command line tools. Long options
+ * cane either include an `=` or leave it out.
+ *
+ * `cake myshell command --connection default --name=something`
+ *
+ * Short options can be defined singally or in groups.
+ *
+ * `cake myshell command -cn`
+ *
+ * ### Positional arguments
+ *
+ * If no positional arguments are defined, all of them will be parsed.  If you define positional
+ * arguments any arguments greater than those defined will cause exceptions.  Additionally you can
+ * declare arguments as optional, by setting the required param to false.
+ *
+ * `$parser->addArgument('model', array('required' => false));`
+ *
+ * ### Providing Help text
+ *
+ * By providing help text for your positional arguments and named arguments, the ConsoleOptionParser
+ * can generate a help display for you.  You can view the help for shells by using the `--help` or `-h` switch.
+ *
+ * @param string $command The command name this parser is for.  The command name is used for generating help.
+ * @param boolean $defaultOptions Whether you want the verbose and quiet options set.
+ */
+	public function __construct($command = null, $defaultOptions = true) {
+		$this->_command = $command;
+
+		$this->addOption('help', array(
+			'short' => 'h',
+			'help' => 'Display this help.',
+			'boolean' => true
+		));
+
+		if ($defaultOptions) {
+			$this->addOption('verbose', array(
+				'short' => 'v',
+				'help' => __('Enable verbose output.'),
+				'boolean' => true
+			))->addOption('quiet', array(
+				'short' => 'q',
+				'help' => __('Enable quiet output.'),
+				'boolean' => true
+			));
+		}
+	}
+
+/**
+ * Static factory method for creating new OptionParsers so you can chain methods off of them.
+ *
+ * @param string $command The command name this parser is for.  The command name is used for generating help.
+ * @param boolean $defaultOptions Whether you want the verbose and quiet options set.
+ * @return ConsoleOptionParser
+ */
+	public static function create($command, $defaultOptions = true) {
+		return new ConsoleOptionParser($command, $defaultOptions);
+	}
+
+/**
+ * Build a parser from an array. Uses an array like
+ *
+ * {{{
+ * $spec = array(
+ *		'description' => 'text',
+ *		'epilog' => 'text',
+ *		'arguments' => array(
+ *			// list of arguments compatible with addArguments.
+ *		),
+ *		'options' => array(
+ *			// list of options compatible with addOptions
+ *		),
+ *		'subcommands' => array(
+ *			// list of subcommands to add.
+ *		)
+ * );
+ * }}}
+ *
+ * @param array $spec The spec to build the OptionParser with.
+ * @return ConsoleOptionParser
+ */
+	public static function buildFromArray($spec) {
+		$parser = new ConsoleOptionParser($spec['command']);
+		if (!empty($spec['arguments'])) {
+			$parser->addArguments($spec['arguments']);
+		}
+		if (!empty($spec['options'])) {
+			$parser->addOptions($spec['options']);
+		}
+		if (!empty($spec['subcommands'])) {
+			$parser->addSubcommands($spec['subcommands']);
+		}
+		if (!empty($spec['description'])) {
+			$parser->description($spec['description']);
+		}
+		if (!empty($spec['epilog'])) {
+			$parser->epilog($spec['epilog']);
+		}
+		return $parser;
+	}
+
+/**
+ * Get or set the command name for shell/task
+ *
+ * @param string $text The text to set, or null if you want to read
+ * @return mixed If reading, the value of the command. If setting $this will be returned
+ */
+	public function command($text = null) {
+		if ($text !== null) {
+			$this->_command = $text;
+			return $this;
+		}
+		return $this->_command;
+	}
+
+/**
+ * Get or set the description text for shell/task
+ *
+ * @param mixed $text The text to set, or null if you want to read. . If an array the text will be imploded with "\n"
+ * @return mixed If reading, the value of the description. If setting $this will be returned
+ */
+	public function description($text = null) {
+		if ($text !== null) {
+			if (is_array($text)) {
+				$text = implode("\n", $text);
+			}
+			$this->_description = $text;
+			return $this;
+		}
+		return $this->_description;
+	}
+
+/**
+ * Get or set an epilog to the parser.  The epilog is added to the end of
+ * the options and arguments listing when help is generated.
+ *
+ * @param mixed $text Text when setting or null when reading. If an array the text will be imploded with "\n"
+ * @return mixed If reading, the value of the epilog. If setting $this will be returned.
+ */
+	public function epilog($text = null) {
+		if ($text !== null) {
+			if (is_array($text)) {
+				$text = implode("\n", $text);
+			}
+			$this->_epilog = $text;
+			return $this;
+		}
+		return $this->_epilog;
+	}
+
+/**
+ * Add an option to the option parser. Options allow you to define optional or required
+ * parameters for your console application. Options are defined by the parameters they use.
+ *
+ * ### Params
+ *
+ * - `short` - The single letter variant for this option, leave undefined for none.
+ * - `help` - Help text for this option.  Used when generating help for the option.
+ * - `default` - The default value for this option. Defaults are added into the parsed params when the
+ *    attached option is not provided or has no value.  Using default and boolean together will not work.
+ *    are added into the parsed parameters when the option is undefined. Defaults to null.
+ * - `boolean` - The option uses no value, its just a boolean switch. Defaults to false.
+ *    If an option is defined as boolean, it will always be added to the parsed params.  If no present
+ *    it will be false, if present it will be true.
+ * - `choices` A list of valid choices for this option.  If left empty all values are valid..
+ *   An exception will be raised when parse() encounters an invalid value.
+ *
+ * @param string $name The long name you want to the value to be parsed out as when options are parsed.
+ * @param array $params An array of parameters that define the behavior of the option
+ * @return returns $this.
+ */
+	public function addOption($name, $params = array()) {
+		$defaults = array(
+			'name' => $name,
+			'short' => null,
+			'help' => '',
+			'default' => null,
+			'boolean' => false,
+			'choices' => array()
+		);
+		$options = array_merge($defaults, $params);
+		$this->_options[$name] = new ConsoleInputOption($options);
+		if (!empty($options['short'])) {
+			$this->_shortOptions[$options['short']] = $name;
+		}
+		return $this;
+	}
+
+/**
+ * Add a positional argument to the option parser.
+ *
+ * ### Params
+ *
+ * - `help` The help text to display for this argument.
+ * - `required` Whether this parameter is required.
+ * - `index` The index for the arg, if left undefined the argument will be put
+ *   onto the end of the arguments. If you define the same index twice the first
+ *   option will be overwritten.
+ * - `choices` A list of valid choices for this argument.  If left empty all values are valid..
+ *   An exception will be raised when parse() encounters an invalid value.
+ *
+ * @param string $name The name of the argument.
+ * @param array $params Parameters for the argument, see above.
+ * @return $this.
+ */
+	public function addArgument($name, $params = array()) {
+		$defaults = array(
+			'name' => $name,
+			'help' => '',
+			'index' => count($this->_args),
+			'required' => false,
+			'choices' => array()
+		);
+		$options = array_merge($defaults, $params);
+		$index = $options['index'];
+		unset($options['index']);
+
+		$this->_args[$index] = new ConsoleInputArgument($options);
+		return $this;
+	}
+
+/**
+ * Add multiple arugments at once. Take an array of arugment defintions.
+ * The keys are used as the argument names, and the values as params for the argument.
+ *
+ * @param array $args Array of arguments to add.
+ * @see ConsoleOptionParser::addArgument()
+ * @return $this
+ */
+	public function addArguments(array $args) {
+		foreach ($args as $name => $params) {
+			$this->addArgument($name, $params);
+		}
+		return $this;
+	}
+
+/**
+ * Add multiple options at once. Takes an array of option definitions.
+ * The keys are used as option names, and the values as params for the option.
+ *
+ * @param array $options Array of options to add.
+ * @see ConsoleOptionParser::addOption()
+ * @return $this
+ */
+	public function addOptions(array $options) {
+		foreach ($options as $name => $params) {
+			$this->addOption($name, $params);
+		}
+		return $this;
+	}
+
+/**
+ * Append a subcommand to the subcommand list.
+ * Subcommands are usually methods on your Shell, but can also be used to document
+ * Tasks
+ *
+ * ### Params
+ *
+ * - `help` - Help text for the subcommand.
+ * - `parser` - A ConsoleOptionParser for the subcommand.  This allows you to create method
+ *    specific option parsers.  When help is generated for a subcommand, if a parser is present
+ *    it will be used.
+ *
+ * @param string $name Name of the subcommand
+ * @param array $params Array of params, see above.
+ * @return $this.
+ */
+	public function addSubcommand($name, $params = array()) {
+		$defaults = array(
+			'name' => $name,
+			'help' => '',
+			'parser' => null
+		);
+		$options = array_merge($defaults, $params);
+		$this->_subcommands[$name] = new ConsoleInputSubcommand($options);
+		return $this;
+	}
+
+/**
+ * Add multiple subcommands at once.
+ *
+ * @param array $commands Array of subcommands.
+ * @return $this
+ */
+	public function addSubcommands(array $commands) {
+		foreach ($commands as $name => $params) {
+			$this->addSubcommand($name, $params);
+		}
+		return $this;
+	}
+
+/**
+ * Gets the arguments defined in the parser.
+ *
+ * @return array Array of argument descriptions
+ */
+	public function arguments() {
+		return $this->_args;
+	}
+
+/**
+ * Get the defined options in the parser.
+ *
+ * @return array
+ */
+	public function options() {
+		return $this->_options;
+	}
+
+/**
+ * Get the array of defined subcommands
+ *
+ * @return array
+ */
+	public function subcommands() {
+		return $this->_subcommands;
+	}
+
+/**
+ * Parse the argv array into a set of params and args.  If $command is not null
+ * and $command is equal to a subcommand that has a parser, that parser will be used
+ * to parse the $argv
+ *
+ * @param array $argv Array of args (argv) to parse.
+ * @param string $command The subcommand to use.  If this parameter is a subcommand, that has a parser,
+ *    That parser will be used to parse $argv instead.
+ * @return Array array($params, $args)
+ * @throws InvalidArgumentException When an invalid parameter is encountered.
+ *   RuntimeException when required arguments are not supplied.
+ */
+	public function parse($argv, $command = null) {
+		if (isset($this->_subcommands[$command]) && $this->_subcommands[$command]->parser()) {
+			return $this->_subcommands[$command]->parser()->parse($argv);
+		}
+		$params = $args = array();
+		$this->_tokens = $argv;
+		while ($token = array_shift($this->_tokens)) {
+			if (substr($token, 0, 2) == '--') {
+				$params = $this->_parseLongOption($token, $params);
+			} elseif (substr($token, 0, 1) == '-') {
+				$params = $this->_parseShortOption($token, $params);
+			} else {
+				$args = $this->_parseArg($token, $args);
+			}
+		}
+		foreach ($this->_args as $i => $arg) {
+			if ($arg->isRequired() && !isset($args[$i]) && empty($params['help'])) {
+				throw new RuntimeException(
+					sprintf(__('Missing required arguments. %s is required.'), $arg->name())
+				);
+			}
+		}
+		foreach ($this->_options as $option) {
+			$name = $option->name();
+			$isBoolean = $option->isBoolean();
+			$default = $option->defaultValue();
+
+			if ($default !== null && !isset($params[$name]) && !$isBoolean) {
+				$params[$name] = $default;
+			}
+			if ($isBoolean && !isset($params[$name])) {
+				$params[$name] = false;
+			}
+		}
+		return array($params, $args);
+	}
+
+/**
+ * Gets formatted help for this parser object.
+ * Generates help text based on the description, options, arguments, subcommands and epilog
+ * in the parser.
+ *
+ * @param string $subcommand If present and a valid subcommand that has a linked parser.
+ *    That subcommands help will be shown instead.
+ * @param int $width The width to format user content to. Defaults to 72
+ * @return string Generated help.
+ */
+	public function help($subcommand = null, $format = 'text', $width = 72) {
+		if (
+			isset($this->_subcommands[$subcommand]) &&
+			$this->_subcommands[$subcommand]->parser() instanceof self
+		) {
+			$subparser = $this->_subcommands[$subcommand]->parser();
+			$subparser->command($this->command() . ' ' . $subparser->command());
+			return $subparser->help(null, $format, $width);
+		}
+		$formatter = new HelpFormatter($this);
+		if ($format == 'text' || $format === true) {
+			return $formatter->text($width);
+		} elseif ($format == 'xml') {
+			return $formatter->xml();
+		}
+	}
+
+/**
+ * Parse the value for a long option out of $this->_tokens.  Will handle
+ * options with an `=` in them.
+ *
+ * @param string $option The option to parse.
+ * @param array $params The params to append the parsed value into
+ * @return array Params with $option added in.
+ */
+	protected function _parseLongOption($option, $params) {
+		$name = substr($option, 2);
+		if (strpos($name, '=') !== false) {
+			list($name, $value) = explode('=', $name, 2);
+			array_unshift($this->_tokens, $value);
+		}
+		return $this->_parseOption($name, $params);
+	}
+
+/**
+ * Parse the value for a short option out of $this->_tokens
+ * If the $option is a combination of multiple shortcuts like -otf
+ * they will be shifted onto the token stack and parsed individually.
+ *
+ * @param string $option The option to parse.
+ * @param array $params The params to append the parsed value into
+ * @return array Params with $option added in.
+ */
+	protected function _parseShortOption($option, $params) {
+		$key = substr($option, 1);
+		if (strlen($key) > 1) {
+			$flags = str_split($key);
+			$key = $flags[0];
+			for ($i = 1, $len = count($flags); $i < $len; $i++) {
+				array_unshift($this->_tokens, '-' . $flags[$i]);
+			}
+		}
+		$name = $this->_shortOptions[$key];
+		return $this->_parseOption($name, $params);
+	}
+
+/**
+ * Parse an option by its name index.
+ *
+ * @param string $option The option to parse.
+ * @param array $params The params to append the parsed value into
+ * @return array Params with $option added in.
+ */
+	protected function _parseOption($name, $params) {
+		if (!isset($this->_options[$name])) {
+			throw new InvalidArgumentException(sprintf(__('Unknown option `%s`'), $name));
+		}
+		$option = $this->_options[$name];
+		$isBoolean = $option->isBoolean();
+		$nextValue = $this->_nextToken();
+		if (!$isBoolean && !empty($nextValue) && $nextValue{0} != '-') {
+			array_shift($this->_tokens);
+			$value = $nextValue;
+		} elseif ($isBoolean) {
+			$value = true;
+		} else {
+			$value = $option->defaultValue();
+		}
+		if ($option->validChoice($value)) {
+			$params[$name] = $value;
+			return $params;
+		}
+	}
+
+/**
+ * Parse an argument, and ensure that the argument doesn't exceed the number of arguments
+ * and that the argument is a valid choice.
+ *
+ * @param string $argument The argument to append
+ * @param array $args The array of parsed args to append to.
+ * @return array Args
+ */
+	protected function _parseArg($argument, $args) {
+		if (empty($this->_args)) {
+			array_push($args, $argument);
+			return $args;
+		}
+		$next = count($args);
+		if (!isset($this->_args[$next])) {
+			throw new InvalidArgumentException(__('Too many arguments.'));
+		}
+
+		if ($this->_args[$next]->validChoice($argument)) {
+			array_push($args, $argument);
+			return $args;
+		}
+	}
+
+/**
+ * Find the next token in the argv set.
+ *
+ * @param string
+ * @return next token or ''
+ */
+	protected function _nextToken() {
+		return isset($this->_tokens[0]) ? $this->_tokens[0] : '';
+	}
+
+}
diff --git a/cake/console/libs/console_output.php b/cake/console/libs/console_output.php
new file mode 100644
index 000000000..ef47266f0
--- /dev/null
+++ b/cake/console/libs/console_output.php
@@ -0,0 +1,289 @@
+<?php
+/**
+ * ConsoleOutput file.
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * Object wrapper for outputing information from a shell application.
+ * Can be connected to any stream resource that can be used with fopen()
+ * 
+ * Can generate colourzied output on consoles that support it. There are a few 
+ * built in styles
+ *
+ * - `error` Error messages.
+ * - `warning` Warning messages.
+ * - `info` Informational messages.
+ * - `comment` Additional text.
+ * - `question` Magenta text used for user prompts
+ *
+ * By defining styles with addStyle() you can create custom console styles.
+ *
+ * ### Using styles in output
+ *
+ * You can format console output using tags with the name of the style to apply. From inside a shell object
+ *
+ * `$this->out('<warning>Overwrite:</warning> foo.php was overwritten.');`
+ *
+ * This would create orange 'Overwrite:' text, while the rest of the text would remain the normal colour.
+ * See ConsoleOutput::styles() to learn more about defining your own styles.  Nested styles are not supported
+ * at this time.
+ *
+ * @package cake.console
+ */
+class ConsoleOutput {
+/**
+ * Raw output constant - no modification of output text.
+ */
+	const RAW = 0;
+
+/**
+ * Plain output - tags will be stripped.
+ */
+	const PLAIN = 1;
+
+/**
+ * Colour output - Convert known tags in to ANSI color escape codes.
+ */
+	const COLOR = 2;
+
+/**
+ * Constant for a newline.
+ */
+	const LF = PHP_EOL;
+
+/**
+ * File handle for output.
+ *
+ * @var resource
+ */
+	protected $_output;
+
+/**
+ * The current output type. Manipulated with ConsoleOutput::outputAs();
+ *
+ * @var integer.
+ */
+	protected $_outputAs = self::COLOR;
+
+/**
+ * text colors used in coloured output.
+ *
+ * @var array
+ */
+	protected static $_foregroundColors = array(
+		'black' => 30,
+		'red' => 31,
+		'green' => 32,
+		'yellow' => 33,
+		'blue' => 34,
+		'magenta' => 35,
+		'cyan' => 36,
+		'white' => 37
+	);
+
+/**
+ * background colours used in coloured output.
+ *
+ * @var array
+ */
+	protected static $_backgroundColors = array(
+		'black' => 40,
+		'red' => 41,
+		'green' => 42,
+		'yellow' => 43,
+		'blue' => 44,
+		'magenta' => 45,
+		'cyan' => 46,
+		'white' => 47
+	);
+
+/**
+ * formatting options for coloured output
+ *
+ * @var string
+ */
+	protected static $_options = array(
+		'bold' => 1,
+		'underline' => 4,
+		'blink' => 5,
+		'reverse' => 7,
+	);
+
+/**
+ * Styles that are available as tags in console output.
+ * You can modify these styles with ConsoleOutput::styles()
+ *
+ * @var array
+ */
+	protected static $_styles = array(
+		'error' => array('text' => 'red', 'underline' => true),
+		'warning' => array('text' => 'yellow'),
+		'info' => array('text' => 'cyan'),
+		'success' => array('text' => 'green'),
+		'comment' => array('text' => 'blue'),
+		'question' => array('text' => "magenta"),
+	);
+
+/**
+ * Construct the output object.
+ *
+ * Checks for a pretty console enviornment.  Ansicon allows pretty consoles
+ * on windows, and is supported.
+ *
+ * @return void
+ */
+	public function __construct($stream = 'php://stdout') {
+		$this->_output = fopen($stream, 'w');
+
+		if (DS == '\\' && !(bool)env('ANSICON')) {
+			$this->_outputAs = self::PLAIN;
+		}
+	}
+
+/**
+ * Outputs a single or multiple messages to stdout. If no parameters
+ * are passed outputs just a newline.
+ *
+ * @param mixed $message A string or a an array of strings to output
+ * @param integer $newlines Number of newlines to append
+ * @return integer Returns the number of bytes returned from writing to stdout.
+ */
+	public function write($message, $newlines = 1) {
+		if (is_array($message)) {
+			$message = implode(self::LF, $message);
+		}
+		return $this->_write($this->styleText($message . str_repeat(self::LF, $newlines)));
+	}
+
+/**
+ * Apply styling to text.
+ *
+ * @param string $text Text with styling tags.
+ * @return string String with color codes added.
+ */
+	public function styleText($text) {
+		if ($this->_outputAs == self::RAW) {
+			return $text;
+		}
+		if ($this->_outputAs == self::PLAIN) {
+			return strip_tags($text);
+		}
+		return preg_replace_callback(
+			'/<(?<tag>[a-z0-9-_]+)>(?<text>.*?)<\/(\1)>/ims', array($this, '_replaceTags'), $text
+		);
+	}
+
+/**
+ * Replace tags with color codes.
+ *
+ * @param array $matches.
+ * @return string
+ */
+	protected function _replaceTags($matches) {
+		$style = $this->styles($matches['tag']);
+		if (empty($style)) {
+			return '<' . $matches['tag'] . '>' . $matches['text'] . '</' . $matches['tag'] . '>';
+		}
+
+		$styleInfo = array();
+		if (!empty($style['text']) && isset(self::$_foregroundColors[$style['text']])) {
+			$styleInfo[] = self::$_foregroundColors[$style['text']];
+		}
+		if (!empty($style['background']) && isset(self::$_backgroundColors[$style['background']])) {
+			$styleInfo[] = self::$_backgroundColors[$style['background']];
+		}
+		unset($style['text'], $style['background']);
+		foreach ($style as $option => $value) {
+			if ($value) {
+				$styleInfo[] = self::$_options[$option];
+			}
+		}
+		return "\033[" . implode($styleInfo, ';') . 'm' . $matches['text'] . "\033[0m";
+	}
+
+/**
+ * Writes a message to the output stream
+ *
+ * @param string $message Message to write.
+ * @return boolean success
+ */
+	protected function _write($message) {
+		return fwrite($this->_output, $message);
+	}
+
+/**
+ * Get the current styles offered, or append new ones in.
+ *
+ * ### Get a style definition
+ *
+ * `$this->output->styles('error');`
+ *
+ * ### Get all the style definitions
+ *
+ * `$this->output->styles();`
+ *
+ * ### Create or modify an existing style
+ *
+ * `$this->output->styles('annoy', array('text' => 'purple', 'background' => 'yellow', 'blink' => true));`
+ *
+ * ### Remove a style
+ *
+ * `$this->output->styles('annoy', false);`
+ *
+ * @param string $style The style to get or create.
+ * @param mixed $definition The array definition of the style to change or create a style
+ *   or false to remove a style.
+ * @return mixed If you are getting styles, the style or null will be returned. If you are creating/modifying
+ *   styles true will be returned.
+ */
+	function styles($style = null, $definition = null) {
+		if ($style === null && $definition === null) {
+			return self::$_styles;
+		}
+		if (is_string($style) && $definition === null) {
+			return isset(self::$_styles[$style]) ? self::$_styles[$style] : null;
+		}
+		if ($definition === false) {
+			unset(self::$_styles[$style]);
+			return true;
+		}
+		self::$_styles[$style] = $definition;
+		return true;
+	}
+
+/**
+ * Get/Set the output type to use.  The output type how formatting tags are treated.
+ * 
+ * @param int $type The output type to use.  Should be one of the class contstants.
+ * @return mixed Either null or the value if getting.
+ */
+	public function outputAs($type = null) {
+		if ($type === null) {
+			return $this->_outputAs;
+		}
+		$this->_outputAs = $type;
+	}
+
+/**
+ * clean up and close handles
+ *
+ * @return void
+ */
+	public function __destruct() {
+		fclose($this->_output);
+	}
+}
\ No newline at end of file
diff --git a/cake/console/libs/help_formatter.php b/cake/console/libs/help_formatter.php
new file mode 100644
index 000000000..7af4cadf1
--- /dev/null
+++ b/cake/console/libs/help_formatter.php
@@ -0,0 +1,176 @@
+<?php
+/**
+ * HelpFormatter
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+/**
+ * HelpFormatter formats help for console shells.  Can format to either
+ * text or XML formats.  Uses ConsoleOptionParser methods to generate help.
+ *
+ * Generally not directly used. Using $parser->help($command, 'xml'); is usually
+ * how you would access help.  Or via the `--help=xml` option on the command line.
+ *
+ * Xml output is useful for intergration with other tools like IDE's or other build tools.
+ *
+ */
+class HelpFormatter {
+/**
+ * Build the help formatter for a an OptionParser
+ *
+ * @return void
+ */
+	public function __construct(ConsoleOptionParser $parser) {
+		$this->_parser = $parser;
+	}
+
+/**
+ * Get the help as formatted text suitable for output on the command line.
+ *
+ * @param integer $width The width of the help output.
+ * @return string
+ */
+	public function text($width = 72) {
+		$parser = $this->_parser;
+		$out = array();
+		$description = $parser->description();
+		if (!empty($description)) {
+			$out[] = String::wrap($description, $width);
+			$out[] = '';
+		}
+		$out[] = '<info>Usage:</info>';
+		$out[] = $this->_generateUsage();
+		$out[] = '';
+		$subcommands = $parser->subcommands();
+		if (!empty($subcommands)) {
+			$out[] = '<info>Subcommands:</info>';
+			$out[] = '';
+			$max = $this->_getMaxLength($subcommands) + 2;
+			foreach ($subcommands as $command) {
+				$out[] = String::wrap($command->help($max), array(
+					'width' => $width,
+					'indent' => str_repeat(' ', $max),
+					'indentAt' => 1
+				));
+			}
+			$out[] = '';
+			$out[] = sprintf(
+				__('To see help on a subcommand use <info>`cake %s [subcommand] --help`</info>'),
+				$parser->command()
+			);
+			$out[] = '';
+		}
+
+		$options = $parser->options();
+		if (!empty($options)) {
+			$max = $this->_getMaxLength($options) + 8;
+			$out[] = '<info>Options:</info>';
+			$out[] = '';
+			foreach ($options as $option) {
+				$out[] = String::wrap($option->help($max), array(
+					'width' => $width,
+					'indent' => str_repeat(' ', $max),
+					'indentAt' => 1
+				));
+			}
+			$out[] = '';
+		}
+
+		$arguments = $parser->arguments();
+		if (!empty($arguments)) {
+			$max = $this->_getMaxLength($arguments) + 2;
+			$out[] = '<info>Arguments:</info>';
+			$out[] = '';
+			foreach ($arguments as $argument) {
+				$out[] = String::wrap($argument->help($max), array(
+					'width' => $width,
+					'indent' => str_repeat(' ', $max),
+					'indentAt' => 1
+				));
+			}
+			$out[] = '';
+		}
+		$epilog = $parser->epilog();
+		if (!empty($epilog)) {
+			$out[] = String::wrap($epilog, $width);
+			$out[] = '';
+		}
+		return implode("\n", $out);
+	}
+
+/**
+ * Generate the usage for a shell based on its arguments and options.
+ * Usage strings favour short options over the long ones. and optional args will
+ * be indicated with []
+ *
+ * @return string
+ */
+	protected function _generateUsage() {
+		$usage = array('cake ' . $this->_parser->command());
+		$subcommands = $this->_parser->subcommands();
+		if (!empty($subcommands)) {
+			$usage[] = '[subcommand]';
+		}
+		foreach ($this->_parser->options() as $option) {
+			$usage[] = $option->usage();
+		}
+		foreach ($this->_parser->arguments() as $argument) {
+			$usage[] = $argument->usage();
+		}
+		return implode(' ', $usage);
+	}
+
+/**
+ * Iterate over a collection and find the longest named thing.
+ *
+ * @return integer
+ */
+	protected function _getMaxLength($collection) {
+		$max = 0;
+		foreach ($collection as $item) {
+			$max = (strlen($item->name()) > $max) ? strlen($item->name()) : $max;
+		}
+		return $max;
+	}
+
+/**
+ * Get the help as an xml string.
+ *
+ * @param boolean $string Return the SimpleXml object or a string.  Defaults to true.
+ * @return mixed. See $string
+ */
+	public function xml($string = true) {
+		$parser = $this->_parser;
+		$xml = new SimpleXmlElement('<shell></shell>');
+		$xml->addChild('commmand', $parser->command());
+		$xml->addChild('description', $parser->description());
+		
+		$xml->addChild('epilog', $parser->epilog());
+		$subcommands = $xml->addChild('subcommands');
+		foreach ($parser->subcommands() as $command) {
+			$command->xml($subcommands);
+		}
+		$options = $xml->addChild('options');
+		foreach ($parser->options() as $option) {
+			$option->xml($options);
+		}
+		$arguments = $xml->addChild('arguments');
+		foreach ($parser->arguments() as $argument) {
+			$argument->xml($arguments);
+		}
+		return $string ? $xml->asXml() : $xml;
+	}
+}
\ No newline at end of file
diff --git a/cake/console/libs/task_collection.php b/cake/console/libs/task_collection.php
index d1ec21733..d28b13b6d 100644
--- a/cake/console/libs/task_collection.php
+++ b/cake/console/libs/task_collection.php
@@ -20,11 +20,18 @@ App::import('Core', 'ObjectCollection');
 
 class TaskCollection extends ObjectCollection {
 /**
- * Shell Dispatcher to give to tasks. and use to find tasks.
+ * Shell to use to set params to tasks.
  *
  * @var array
  */
-	protected $_Dispatch;
+	protected $_Shell;
+
+/**
+ * The directory inside each shell path that contains tasks.
+ *
+ * @var string
+ */
+	public $taskPathPrefix = 'tasks/';
 
 /**
  * Constructor
@@ -32,11 +39,13 @@ class TaskCollection extends ObjectCollection {
  * @param array $paths Array of paths to search for tasks on .
  * @return void
  */
-	public function __construct(ShellDispatcher $Dispatcher) {
-		$this->_Dispatch = $Dispatcher;
+	public function __construct(Shell $Shell) {
+		$this->_Shell = $Shell;
 	}
+
 /**
- * Loads/constructs a task.  Will return the instance in the registry if it already exists.
+ * Loads/constructs a task.  Will return the instance in the collection
+ * if it already exists.
  * 
  * @param string $task Task name to load
  * @param array $settings Settings for the task.
@@ -53,35 +62,21 @@ class TaskCollection extends ObjectCollection {
 		$taskFile = Inflector::underscore($name);
 		$taskClass = $name . 'Task';
 		if (!class_exists($taskClass)) {
-			$taskFile = $this->_getPath($taskFile);
-			require_once $taskFile;
+			if (!App::import('Shell', $plugin . $this->taskPathPrefix . $name)) {
+				throw new MissingTaskFileException($taskFile . '.php');
+			}
 			if (!class_exists($taskClass)) {
 				throw new MissingTaskClassException($taskClass);
 			}
 		}
 
-		$this->_loaded[$name] = new $taskClass($this->_Dispatch);
+		$this->_loaded[$name] = new $taskClass(
+			$this->_Shell->stdout, $this->_Shell->stderr, $this->_Shell->stdin
+		);
 		if ($enable === true) {
 			$this->_enabled[] = $name;
 		}
 		return $this->_loaded[$name];
 	}
 
-/**
- * Find a task file on one of the paths.
- *
- * @param string $file Underscored name of the file to find missing .php
- * @return string Filename to the task
- * @throws MissingTaskFileException
- */
-	protected function _getPath($file) {
-		foreach ($this->_Dispatch->shellPaths as $path) {
-			$taskPath = $path . 'tasks' . DS . $file . '.php';
-			if (file_exists($taskPath)) {
-				return $taskPath;
-			}
-		}
-		throw new MissingTaskFileException($file . '.php');
-	}
-
 }
diff --git a/cake/console/shell_dispatcher.php b/cake/console/shell_dispatcher.php
new file mode 100644
index 000000000..5b1b6a8fe
--- /dev/null
+++ b/cake/console/shell_dispatcher.php
@@ -0,0 +1,327 @@
+<?php
+/**
+ * ShellDispatcher file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Shell dispatcher handles dispatching cli commands.
+ *
+ * @package       cake
+ * @subpackage    cake.cake.console
+ */
+class ShellDispatcher {
+
+/**
+ * Contains command switches parsed from the command line.
+ *
+ * @var array
+ * @access public
+ */
+	public $params = array();
+
+/**
+ * Contains arguments parsed from the command line.
+ *
+ * @var array
+ * @access public
+ */
+	public $args = array();
+
+/**
+ * Constructor
+ *
+ * The execution of the script is stopped after dispatching the request with
+ * a status code of either 0 or 1 according to the result of the dispatch.
+ *
+ * @param array $args the argv
+ * @return void
+ */
+	public function __construct($args = array(), $bootstrap = true) {
+		set_time_limit(0);
+
+		if ($bootstrap) {
+			$this->__initConstants();
+		}
+		$this->parseParams($args);
+		if ($bootstrap) {
+			$this->_initEnvironment();
+		}
+	}
+
+/**
+ * Run the dispatcher
+ *
+ * @return void
+ */
+	public static function run($argv) {
+		$dispatcher = new ShellDispatcher($argv);
+		$dispatcher->_stop($dispatcher->dispatch() === false ? 1 : 0);
+	}
+
+/**
+ * Defines core configuration.
+ *
+ * @access private
+ */
+	function __initConstants() {
+		if (function_exists('ini_set')) {
+			ini_set('display_errors', '1');
+			ini_set('error_reporting', E_ALL & ~E_DEPRECATED);
+			ini_set('html_errors', false);
+			ini_set('implicit_flush', true);
+			ini_set('max_execution_time', 0);
+		}
+
+		if (!defined('CAKE_CORE_INCLUDE_PATH')) {
+			define('DS', DIRECTORY_SEPARATOR);
+			define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__))));
+			define('DISABLE_DEFAULT_ERROR_HANDLING', false);
+			define('CAKEPHP_SHELL', true);
+			if (!defined('CORE_PATH')) {
+				if (function_exists('ini_set') && ini_set('include_path', CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ini_get('include_path'))) {
+					define('CORE_PATH', null);
+				} else {
+					define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
+				}
+			}
+		}
+	}
+
+/**
+ * Defines current working environment.
+ *
+ */
+	protected function _initEnvironment() {
+		if (!$this->__bootstrap()) {
+			$message = "Unable to load CakePHP core.\nMake sure " . DS . 'cake' . DS . 'libs exists in ' . CAKE_CORE_INCLUDE_PATH;
+			throw new RuntimeException($message);
+		}
+
+		if (!isset($this->args[0]) || !isset($this->params['working'])) {
+			$message = "This file has been loaded incorrectly and cannot continue.\n" .
+				"Please make sure that " . DIRECTORY_SEPARATOR . "cake" . DIRECTORY_SEPARATOR . "console is in your system path,\n" . 
+				"and check the cookbook for the correct usage of this command.\n" .
+				"(http://book.cakephp.org/)";
+			throw new RuntimeException($message);
+		}
+
+		$this->shiftArgs();
+	}
+
+/**
+ * Initializes the environment and loads the Cake core.
+ *
+ * @return boolean Success.
+ * @access private
+ */
+	function __bootstrap() {
+
+		define('ROOT', $this->params['root']);
+		define('APP_DIR', $this->params['app']);
+		define('APP_PATH', $this->params['working'] . DS);
+		define('WWW_ROOT', APP_PATH . $this->params['webroot'] . DS);
+		if (!is_dir(ROOT . DS . APP_DIR . DS . 'tmp')) {
+			define('TMP', CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'tmp' . DS);
+		}
+
+		$boot = file_exists(ROOT . DS . APP_DIR . DS . 'config' . DS . 'bootstrap.php');
+		require CORE_PATH . 'cake' . DS . 'bootstrap.php';
+		require_once CONSOLE_LIBS . 'console_error_handler.php';
+		set_exception_handler(array('ConsoleErrorHandler', 'handleException'));
+
+		if (!file_exists(APP_PATH . 'config' . DS . 'core.php')) {
+			include_once CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'console' . DS . 'templates' . DS . 'skel' . DS . 'config' . DS . 'core.php';
+			App::build();
+		}
+		if (!defined('FULL_BASE_URL')) {
+			define('FULL_BASE_URL', '/');
+		}
+
+		return true;
+	}
+
+/**
+ * Dispatches a CLI request
+ *
+ * @return boolean
+ */
+	public function dispatch() {
+		$shell = $this->shiftArgs();
+
+		if (!$shell) {
+			$this->help();
+			return false;
+		}
+		if (in_array($shell, array('help', '--help', '-h'))) {
+			$this->help();
+			return true;
+		}
+
+		$Shell = $this->_getShell($shell);
+
+		$command = null;
+		if (isset($this->args[0])) {
+			$command = $this->args[0];
+		}
+
+		if ($Shell instanceof Shell) {
+			$Shell->initialize();
+			$Shell->loadTasks();
+			return $Shell->runCommand($command, $this->args);
+		}
+		$methods = array_diff(get_class_methods($Shell), get_class_methods('Shell'));
+		$added = in_array($command, $methods);
+		$private = $command[0] == '_' && method_exists($Shell, $command);
+
+		if (!$private) {
+			if ($added) {
+				$this->shiftArgs();
+				$Shell->startup();
+				return $Shell->{$command}();
+			}
+			if (method_exists($Shell, 'main')) {
+				$Shell->startup();
+				return $Shell->main();
+			}
+		}
+		throw new MissingShellMethodException(array('shell' => $shell, 'method' => $arg));
+	}
+
+/**
+ * Get shell to use, either plugin shell or application shell
+ *
+ * All paths in the loaded shell paths are searched.
+ *
+ * @param string $shell Optionally the name of a plugin
+ * @return mixed False if no shell could be found or an object on success
+ * @throws MissingShellFileException, MissingShellClassException when errors are encountered.
+ */
+	protected function _getShell($shell) {
+		list($plugin, $shell) = pluginSplit($shell, true);
+
+		$loaded = App::import('Shell', $plugin . $shell);
+		$class = Inflector::camelize($shell) . 'Shell';
+	
+		if (!$loaded) {
+			throw new MissingShellFileException(array('shell' => $shell));
+		}
+		if (!class_exists($class)) {
+			throw new MissingShellClassException(array('shell' => $class));
+		}
+		$Shell = new $class();
+		return $Shell;
+	}
+
+/**
+ * Parses command line options and extracts the directory paths from $params
+ *
+ * @param array $params Parameters to parse
+ */
+	public function parseParams($args) {
+		$this->_parsePaths($args);
+
+		$defaults = array(
+			'app' => 'app', 
+			'root' => dirname(dirname(dirname(__FILE__))),
+			'working' => null, 
+			'webroot' => 'webroot'
+		);
+		$params = array_merge($defaults, array_intersect_key($this->params, $defaults));
+		$isWin = false;
+		foreach ($defaults as $default => $value) {
+			if (strpos($params[$default], '\\') !== false) {
+				$isWin = true;
+				break;
+			}
+		}
+		$params = str_replace('\\', '/', $params);
+
+		if (!empty($params['working']) && (!isset($this->args[0]) || isset($this->args[0]) && $this->args[0]{0} !== '.')) {
+			if (empty($this->params['app']) && $params['working'] != $params['root']) {
+				$params['root'] = dirname($params['working']);
+				$params['app'] = basename($params['working']);
+			} else {
+				$params['root'] = $params['working'];
+			}
+		}
+
+		if ($params['app'][0] == '/' || preg_match('/([a-z])(:)/i', $params['app'], $matches)) {
+			$params['root'] = dirname($params['app']);
+		} elseif (strpos($params['app'], '/')) {
+			$params['root'] .= '/' . dirname($params['app']);
+		}
+
+		$params['app'] = basename($params['app']);
+		$params['working'] = rtrim($params['root'], '/') . '/' . $params['app'];
+
+		if (!empty($matches[0]) || !empty($isWin)) {
+			$params = str_replace('/', '\\', $params);
+		}
+
+		$this->params = array_merge($this->params, $params);
+	}
+
+/**
+ * Parses out the paths from from the argv
+ *
+ * @return void
+ */
+	protected function _parsePaths($args) {
+		$parsed = array();
+		$keys = array('-working', '--working', '-app', '--app', '-root', '--root');
+		foreach ($keys as $key) {
+			$index = array_search($key, $args);
+			if ($index !== false) {
+				$keyname = str_replace('-', '', $key);
+				$valueIndex = $index + 1;
+				$parsed[$keyname] = $args[$valueIndex];
+				array_splice($args, $index, 2);
+			}
+		}
+		$this->args = $args;
+		$this->params = $parsed;
+	}
+
+/**
+ * Removes first argument and shifts other arguments up
+ *
+ * @return mixed Null if there are no arguments otherwise the shifted argument
+ */
+	public function shiftArgs() {
+		return array_shift($this->args);
+	}
+
+/**
+ * Shows console help.  Performs an internal dispatch to the CommandList Shell
+ *
+ */
+	public function help() {
+		$this->args = array_merge(array('command_list'), $this->args);
+		$this->dispatch();
+	}
+
+/**
+ * Stop execution of the current script
+ *
+ * @param $status see http://php.net/exit for values
+ * @return void
+ */
+	protected function _stop($status = 0) {
+		exit($status);
+	}
+}
\ No newline at end of file
diff --git a/cake/console/libs/acl.php b/cake/console/shells/acl.php
similarity index 61%
rename from cake/console/libs/acl.php
rename to cake/console/shells/acl.php
index 3d24c4014..169bd14d2 100644
--- a/cake/console/libs/acl.php
+++ b/cake/console/shells/acl.php
@@ -66,6 +66,7 @@ class AclShell extends Shell {
  *
  */
 	public function startup() {
+		parent::startup();
 		if (isset($this->params['connection'])) {
 			$this->connection = $this->params['connection'];
 		}
@@ -83,7 +84,7 @@ class AclShell extends Shell {
 			$this->_stop();
 		}
 
-		if ($this->command && !in_array($this->command, array('help'))) {
+		if ($this->command) {
 			if (!config('database')) {
 				$this->out(__('Your database configuration was not found. Take a moment to create one.'), true);
 				$this->args = null;
@@ -105,20 +106,7 @@ class AclShell extends Shell {
  *
  */
 	public function main() {
-		$out  = __('Available ACL commands:') . "\n";
-		$out .= "\t - create\n";
-		$out .= "\t - delete\n";
-		$out .= "\t - setParent\n";
-		$out .= "\t - getPath\n";
-		$out .= "\t - check\n";
-		$out .= "\t - grant\n";
-		$out .= "\t - deny\n";
-		$out .= "\t - inherit\n";
-		$out .= "\t - view\n";
-		$out .= "\t - initdb\n";
-		$out .= "\t - help\n\n";
-		$out .= __("For help, run the 'help' command.  For help on a specific command, run 'help <command>'");
-		$this->out($out);
+		$this->out($this->OptionParser->help());
 	}
 
 /**
@@ -126,8 +114,6 @@ class AclShell extends Shell {
  *
  */
 	public function create() {
-		$this->_checkArgs(3, 'create');
-		$this->checkNodeType();
 		extract($this->__dataVars());
 
 		$class = ucfirst($this->args[0]);
@@ -143,13 +129,13 @@ class AclShell extends Shell {
 		if (is_string($data) && $data != '/') {
 			$data = array('alias' => $data);
 		} elseif (is_string($data)) {
-			$this->error(__('/ can not be used as an alias!'), __("\t/ is the root, please supply a sub alias"));
+			$this->error(__('/ can not be used as an alias!') . __("	/ is the root, please supply a sub alias"));
 		}
 
 		$data['parent_id'] = $parent;
 		$this->Acl->{$class}->create();
 		if ($this->Acl->{$class}->save($data)) {
-			$this->out(sprintf(__("New %s '%s' created.\n"), $class, $this->args[2]), true);
+			$this->out(sprintf(__("<success>New %s</success> '%s' created."), $class, $this->args[2]), 2);
 		} else {
 			$this->err(sprintf(__("There was a problem creating a new %s '%s'."), $class, $this->args[2]));
 		}
@@ -160,17 +146,15 @@ class AclShell extends Shell {
  *
  */
 	public function delete() {
-		$this->_checkArgs(2, 'delete');
-		$this->checkNodeType();
 		extract($this->__dataVars());
 
 		$identifier = $this->parseIdentifier($this->args[1]);
 		$nodeId = $this->_getNodeId($class, $identifier);
 
 		if (!$this->Acl->{$class}->delete($nodeId)) {
-			$this->error(__('Node Not Deleted'), sprintf(__('There was an error deleting the %s. Check that the node exists'), $class) . ".\n");
+			$this->error(__('Node Not Deleted') . sprintf(__('There was an error deleting the %s. Check that the node exists'), $class) . ".\n");
 		}
-		$this->out(sprintf(__('%s deleted'), $class) . ".\n", true);
+		$this->out(sprintf(__('<success>%s deleted.</success>'), $class), 2);
 	}
 
 /**
@@ -178,8 +162,6 @@ class AclShell extends Shell {
  *
  */
 	public function setParent() {
-		$this->_checkArgs(3, 'setParent');
-		$this->checkNodeType();
 		extract($this->__dataVars());
 		$target = $this->parseIdentifier($this->args[1]);
 		$parent = $this->parseIdentifier($this->args[2]);
@@ -203,8 +185,6 @@ class AclShell extends Shell {
  *
  */
 	public function getPath() {
-		$this->_checkArgs(2, 'getPath');
-		$this->checkNodeType();
 		extract($this->__dataVars());
 		$identifier = $this->parseIdentifier($this->args[1]);
 
@@ -247,13 +227,12 @@ class AclShell extends Shell {
  *
  */
 	public function check() {
-		$this->_checkArgs(3, 'check');
 		extract($this->__getParams());
 
 		if ($this->Acl->check($aro, $aco, $action)) {
-			$this->out(sprintf(__('%s is allowed.'), $aroName), true);
+			$this->out(sprintf(__('%s is <success>allowed</success>.'), $aroName), true);
 		} else {
-			$this->out(sprintf(__('%s is not allowed.'), $aroName), true);
+			$this->out(sprintf(__('%s is <error>not allowed</error>.'), $aroName), true);
 		}
 	}
 
@@ -262,13 +241,12 @@ class AclShell extends Shell {
  *
  */
 	public function grant() {
-		$this->_checkArgs(3, 'grant');
 		extract($this->__getParams());
 
 		if ($this->Acl->allow($aro, $aco, $action)) {
-			$this->out(__('Permission granted.'), true);
+			$this->out(__('Permission <success>granted</success>.'), true);
 		} else {
-			$this->out(__('Permission was not granted.'), true);
+			$this->out(__('Permission was <error>not granted</error>.'), true);
 		}
 	}
 
@@ -277,7 +255,6 @@ class AclShell extends Shell {
  *
  */
 	public function deny() {
-		$this->_checkArgs(3, 'deny');
 		extract($this->__getParams());
 
 		if ($this->Acl->deny($aro, $aco, $action)) {
@@ -292,7 +269,6 @@ class AclShell extends Shell {
  *
  */
 	public function inherit() {
-		$this->_checkArgs(3, 'inherit');
 		extract($this->__getParams());
 
 		if ($this->Acl->inherit($aro, $aco, $action)) {
@@ -307,8 +283,6 @@ class AclShell extends Shell {
  *
  */
 	public function view() {
-		$this->_checkArgs(1, 'view');
-		$this->checkNodeType();
 		extract($this->__dataVars());
 
 		if (isset($this->args[1])) {
@@ -368,116 +342,168 @@ class AclShell extends Shell {
  *
  */
 	public function initdb() {
-		$this->Dispatch->args = array('schema', 'create', 'DbAcl');
-		$this->Dispatch->dispatch();
+		return $this->dispatchShell('schema create DbAcl');
 	}
 
 /**
- * Show help screen.
+ * Get the option parser.
  *
+ * @return void
  */
-	public function help() {
-		$head = "-----------------------------------------------\n";
-		$head .= __('Usage: cake acl <command> <arg1> <arg2>...') . "\n";
-		$head .= "-----------------------------------------------\n";
-		$head .= __('Commands:') . "\n";
-
-		$commands = array(
-			'create' => "create aro|aco <parent> <node>\n" .
-				"\t" . __("Creates a new ACL object <node> under the parent") . "\n" .
-				"\t" . __("specified by <parent>, an id/alias.") . "\n" .
-				"\t" . __("The <parent> and <node> references can be") . "\n" .
-				"\t" . __("in one of the following formats:") . "\n\n" .
-				"\t\t- " . __("<model>.<id> - The node will be bound to a") . "\n" .
-				"\t\t" . __("specific record of the given model.") . "\n\n" .
-				"\t\t- " . __("<alias> - The node will be given a string alias,") . "\n" .
-				"\t\t" . __(" (or path, in the case of <parent>)") . "\n" .
-				"\t\t  " . __("i.e. 'John'.  When used with <parent>,") . "\n" .
-				"\t\t" . __("this takes the form of an alias path,") . "\n" .
-				"\t\t  " . __("i.e. <group>/<subgroup>/<parent>.") . "\n\n" .
-				"\t" . __("To add a node at the root level,") . "\n" .
-				"\t" . __("enter 'root' or '/' as the <parent> parameter.") . "\n",
-
-			'delete' => "delete aro|aco <node>\n" .
-				"\t" . __("Deletes the ACL object with the given <node> reference") . "\n" .
-				"\t" . __("For more detailed parameter usage info,") . "\n" .
-				"\t" . __("see help for the 'create' command."),
-
-			'setparent' => "setParent aro|aco <node> <parent node>\n" .
-				"\t" . __("Moves the ACL object specified by <node> beneath") . "\n" .
-				"\t" . __("the parent ACL object specified by <parent>.") . "\n" .
-				"\t" . __("For more detailed parameter usage info,") . "\n" .
-				"\t" . __("see help for the 'create' command."),
-
-			'getpath' => "getPath aro|aco <node>\n" .
-				"\t" . __("Returns the path to the ACL object specified by <node>. This command", true) . "\n" .
-				"\t" . __("is useful in determining the inhertiance of permissions for a certain", true) . "\n" .
-				"\t" . __("object in the tree.", true) . "\n" .
-				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
-				"\t" . __("see help for the 'create' command.", true),
-
-			'check' => "check <node> <node> [<aco_action>] " . __("or", true) . " all\n" .
-				"\t" . __("Use this command to check ACL permissions.", true) . "\n" .
-				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
-				"\t" . __("see help for the 'create' command.", true),
-
-			'grant' => "grant <aronode> <aconode> [<aco_action>] " . __("or", true) . " all\n" .
-				"\t" . __("Use this command to grant ACL permissions. Once executed, the ARO", true) . "\n" .
-				"\t" . __("specified (and its children, if any) will have ALLOW access to the", true) . "\n" .
-				"\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" .
-				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
-				"\t" . __("see help for the 'create' command.", true),
-
-			'deny' => "deny <aronode> <aconode> [<aco_action>]" . __("or", true) . " all\n" .
-				"\t" . __("Use this command to deny ACL permissions. Once executed, the ARO", true) . "\n" .
-				"\t" . __("specified (and its children, if any) will have DENY access to the", true) . "\n" .
-				"\t" . __("specified ACO action (and the ACO's children, if any).", true) . "\n" .
-				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
-				"\t" . __("see help for the 'create' command.", true),
-
-			'inherit' => "inherit <aronode> <aconode> [<aco_action>]" . __("or", true) . " all\n" .
-				"\t" . __("Use this command to force a child ARO object to inherit its", true) . "\n" .
-				"\t" . __("permissions settings from its parent.", true) . "\n" .
-				"\t" . __("For more detailed parameter usage info,", true) . "\n" .
-				"\t" . __("see help for the 'create' command.", true),
-
-			'view' => "view aro|aco [<node>]\n" .
-				"\t" . __("The view command will return the ARO or ACO tree.") . "\n" .
-				"\t" . __("The optional node parameter allows you to return") . "\n" .
-				"\t" . __("only a portion of the requested tree.") . "\n" .
-				"\t" . __("For more detailed parameter usage info,") . "\n" .
-				"\t" . __("see help for the 'create' command."),
-
-			'initdb' => "initdb\n".
-				"\t" . __("Uses this command : cake schema run create DbAcl"),
-
-			'help' => "help [<command>]\n" .
-				"\t" . __("Displays this help message, or a message on a specific command.")
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		
+		$type = array(
+			'choices' => array('aro', 'aco'), 
+			'required' => true,
+			'help' => __('Type of node to create.')
 		);
-
-		$this->out($head);
-		if (!isset($this->args[0])) {
-			foreach ($commands as $cmd) {
-				$this->out("{$cmd}\n\n");
-			}
-		} elseif (isset($commands[strtolower($this->args[0])])) {
-			$this->out($commands[strtolower($this->args[0])] . "\n\n");
-		} else {
-			$this->out(sprintf(__("Command '%s' not found"), $this->args[0]));
-		}
-	}
-
-/**
- * Check that first argument specifies a valid Node type (ARO/ACO)
- *
- */
-	public function checkNodeType() {
-		if (!isset($this->args[0])) {
-			return false;
-		}
-		if ($this->args[0] != 'aco' && $this->args[0] != 'aro') {
-			$this->error(sprintf(__("Missing/Unknown node type: '%s'"), $this->args[0]), __('Please specify which ACL object type you wish to create. Either "aro" or "aco"'));
-		}
+		
+		$parser->description('A console tool for managing the DbAcl')
+			->addSubcommand('create', array(
+				'help' => __('Create a new ACL node'),
+				'parser' => array(
+					'description' => __('Creates a new ACL object <node> under the parent'),
+					'arguments' => array(
+						'type' => $type,
+						'parent' => array(
+							'help' => __('The node selector for the parent.'),
+							'required' => true
+						),
+						'alias' => array(
+							'help' => __('The alias to use for the newly created node.'),
+							'required' => true
+						)
+					)
+				)
+			))->addSubcommand('delete', array(
+				'help' => __('Deletes the ACL object with the given <node> reference'),
+				'parser' => array(
+					'description' => __('Delete an ACL node.'),
+					'arguments' => array(
+						'type' => $type,
+						'node' => array(
+							'help' => __('The node identifier to delete.'),
+							'required' => true,
+						)
+					)
+				)
+			))->addSubcommand('setparent', array(
+				'help' => __('Moves the ACL node under a new parent.'),
+				'parser' => array(
+					'description' => __('Moves the ACL object specified by <node> beneath <parent>'),
+					'arguments' => array(
+						'type' => $type,
+						'node' => array(
+							'help' => __('The node to move'),
+							'required' => true,
+						),
+						'parent' => array(
+							'help' => __('The new parent for <node>.'),
+							'required' => true
+						)
+					)
+				)
+			))->addSubcommand('getpath', array(
+				'help' => __('Print out the path to an ACL node.'),
+				'parser' => array(
+					'description' => array(
+						__("Returns the path to the ACL object specified by <node>."),
+						__("This command is useful in determining the inhertiance of permissions"),
+						__("for a certain object in the tree.")
+					),
+					'arguments' => array(
+						'type' => $type,
+						'node' => array(
+							'help' => __('The node to get the path of'),
+							'required' => true,
+						)
+					)
+				)
+			))->addSubcommand('check', array(
+				'help' => __('Check the permissions between an ACO and ARO.'),
+				'parser' => array(
+					'description' => array(
+						__("Use this command to grant ACL permissions. Once executed, the ARO "),
+						__("specified (and its children, if any) will have ALLOW access to the"),
+						__("specified ACO action (and the ACO's children, if any).")
+					),
+					'arguments' => array(
+						'aro' => array('help' => __('ARO to check.'), 'required' => true),
+						'aco' => array('help' => __('ACO to check.'), 'required' => true),
+						'action' => array('help' => __('Action to check'), 'default' => 'all')
+					)
+				)
+			))->addSubcommand('grant', array(
+				'help' => __('Grant an ARO permissions to an ACO.'),
+				'parser' => array(
+					'description' => array(
+						__("Use this command to grant ACL permissions. Once executed, the ARO"),
+						__("specified (and its children, if any) will have ALLOW access to the"),
+						__("specified ACO action (and the ACO's children, if any).")
+					),
+					'arguments' => array(
+						'aro' => array('help' => __('ARO to grant permission to.'), 'required' => true),
+						'aco' => array('help' => __('ACO to grant access to.'), 'required' => true),
+						'action' => array('help' => __('Action to grant'), 'default' => 'all')
+					)
+				)
+			))->addSubcommand('deny', array(
+				'help' => __('Deny an ARO permissions to an ACO.'),
+				'parser' => array(
+					'description' => array(
+						__("Use this command to deny ACL permissions. Once executed, the ARO"),
+						__("specified (and its children, if any) will have DENY access to the"),
+						__("specified ACO action (and the ACO's children, if any).")
+					),
+					'arguments' => array(
+						'aro' => array('help' => __('ARO to deny.'), 'required' => true),
+						'aco' => array('help' => __('ACO to deny.'), 'required' => true),
+						'action' => array('help' => __('Action to deny'), 'default' => 'all')
+					)
+				)
+			))->addSubcommand('inherit', array(
+				'help' => __('Inherit an ARO\'s parent permissions.'),
+				'parser' => array(
+					'description' => array(
+						__("Use this command to force a child ARO object to inherit its"),
+						__("permissions settings from its parent.")
+					),
+					'arguments' => array(
+						'aro' => array('help' => __('ARO to have permisssions inherit.'), 'required' => true),
+						'aco' => array('help' => __('ACO to inherit permissions on.'), 'required' => true),
+						'action' => array('help' => __('Action to inherit'), 'default' => 'all')
+					)
+				)
+			))->addSubcommand('view', array(
+				'help' => __('View a tree or a single node\'s subtree.'),
+				'parser' => array(
+					'description' => array(
+						__("The view command will return the ARO or ACO tree."),
+						__("The optional node parameter allows you to return"),
+						__("only a portion of the requested tree.")
+					),
+					'arguments' => array(
+						'type' => $type,
+						'node' => array('help' => __('The optional node to view the subtree of.'))
+					)
+				)
+			))->addSubcommand('initdb', array(
+				'help' => __('Initialize the DbAcl tables. Uses this command : cake schema run create DbAcl')
+			))->epilog(
+				array(
+					'Node and parent arguments can be in one of the following formats:',
+					'',
+					' - <model>.<id> - The node will be bound to a specific record of the given model.',
+					'',
+					' - <alias> - The node will be given a string alias (or path, in the case of <parent>)',
+					"   i.e. 'John'.  When used with <parent>, this takes the form of an alias path,",
+					"   i.e. <group>/<subgroup>/<parent>.",
+					'',
+					"To add a node at the root level, enter 'root' or '/' as the <parent> parameter."
+				)
+			);
+		return $parser;
 	}
 
 /**
@@ -488,7 +514,7 @@ class AclShell extends Shell {
  * @return boolean Success
  */
 	public function nodeExists() {
-		if (!$this->checkNodeType() && !isset($this->args[1])) {
+		if (!isset($this->args[0]) || !isset($this->args[1])) {
 			return false;
 		}
 		extract($this->__dataVars($this->args[0]));
diff --git a/cake/console/libs/api.php b/cake/console/shells/api.php
similarity index 87%
rename from cake/console/libs/api.php
rename to cake/console/shells/api.php
index 26acdccb0..d0c19da7e 100644
--- a/cake/console/libs/api.php
+++ b/cake/console/shells/api.php
@@ -60,7 +60,7 @@ class ApiShell extends Shell {
  */
 	public function main() {
 		if (empty($this->args)) {
-			return $this->help();
+			return $this->out($this->OptionParser->help());
 		}
 
 		$type = strtolower($this->args[0]);
@@ -78,7 +78,6 @@ class ApiShell extends Shell {
 			$file = Inflector::underscore($this->args[1]);
 			$class = Inflector::camelize($file);
 		}
-
 		$objects = App::objects('class', $path);
 		if (in_array($class, $objects)) {
 			if (in_array($type, array('behavior', 'component', 'helper')) && $type !== $file) {
@@ -88,19 +87,18 @@ class ApiShell extends Shell {
 			}
 
 		} else {
-			$this->err(sprintf(__('%s not found'), $class));
-			$this->_stop();
+			$this->error(sprintf(__('%s not found'), $class));
 		}
 
 		$parsed = $this->__parseClass($path . $file .'.php', $class);
 
 		if (!empty($parsed)) {
-			if (isset($this->params['m'])) {
-				if (!isset($parsed[$this->params['m']])) {
-					$this->err(sprintf(__('%s::%s() could not be found'), $class, $this->params['m']));
+			if (isset($this->params['method'])) {
+				if (!isset($parsed[$this->params['method']])) {
+					$this->err(sprintf(__('%s::%s() could not be found'), $class, $this->params['method']));
 					$this->_stop();
 				}
-				$method = $parsed[$this->params['m']];
+				$method = $parsed[$this->params['method']];
 				$this->out($class .'::'.$method['method'] . $method['parameters']);
 				$this->hr();
 				$this->out($method['comment'], true);
@@ -136,6 +134,23 @@ class ApiShell extends Shell {
 		}
 	}
 
+/**
+ * Get and configure the optionparser.
+ *
+ * @return ConsoleOptionParser
+ */
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		$parser->addArgument('type', array(
+			'help' => 'Either a full path or type of class (model, behavior, controller, component, view, helper)'
+		))->addArgument('className', array(
+			'help' => 'A CakePHP core class name (e.g: Component, HtmlHelper).'
+		))->addOption('method', array(
+			'short' => 'm',
+			'help' => __('The specific method you want help on.')
+		))->description(__('Lookup doc block comments for classes in CakePHP.'));
+		return $parser;
+	}
 /**
  * Show help for this shell.
  *
diff --git a/cake/console/libs/bake.php b/cake/console/shells/bake.php
similarity index 68%
rename from cake/console/libs/bake.php
rename to cake/console/shells/bake.php
index 047dda1c0..dfd048194 100644
--- a/cake/console/libs/bake.php
+++ b/cake/console/shells/bake.php
@@ -43,22 +43,13 @@ class BakeShell extends Shell {
  * Override loadTasks() to handle paths
  *
  */
-	public function loadTasks() {
-		parent::loadTasks();
+	public function startup() {
+		parent::startup();
 		$task = Inflector::classify($this->command);
 		if (isset($this->{$task}) && !in_array($task, array('Project', 'DbConfig'))) {
 			if (isset($this->params['connection'])) {
 				$this->{$task}->connection = $this->params['connection'];
 			}
-			foreach($this->args as $i => $arg) {
-				if (strpos($arg, '.')) {
-					list($this->params['plugin'], $this->args[$i]) = pluginSplit($arg);
-					break;
-				}
-			}
-			if (isset($this->params['plugin'])) {
-				$this->{$task}->plugin = $this->params['plugin'];
-			}
 		}
 	}
 
@@ -68,8 +59,9 @@ class BakeShell extends Shell {
  */
 	public function main() {
 		if (!is_dir($this->DbConfig->path)) {
-			if ($this->Project->execute()) {
-				$this->DbConfig->path = $this->params['working'] . DS . 'config' . DS;
+			$path = $this->Project->execute();
+			if (!empty($path)) {
+				$this->DbConfig->path = $path . 'config' . DS;
 			} else {
 				return false;
 			}
@@ -188,38 +180,54 @@ class BakeShell extends Shell {
 			$this->out(__('Bake All complete'));
 			array_shift($this->args);
 		} else {
-			$this->err(__('Bake All could not continue without a valid model'));
+			$this->error(__('Bake All could not continue without a valid model'));
 		}
 		$this->_stop();
 	}
 
 /**
- * Displays help contents
+ * get the option parser.
  *
+ * @return void
  */
-	public function help() {
-		$this->out('CakePHP Bake:');
-		$this->hr();
-		$this->out('The Bake script generates controllers, views and models for your application.');
-		$this->out('If run with no command line arguments, Bake guides the user through the class');
-		$this->out('creation process. You can customize the generation process by telling Bake');
-		$this->out('where different parts of your application are using command line arguments.');
-		$this->hr();
-		$this->out("Usage: cake bake <command> <arg1> <arg2>...");
-		$this->hr();
-		$this->out('Params:');
-		$this->out("\t-app <path> Absolute/Relative path to your app folder.\n");
-		$this->out('Commands:');
-		$this->out("\n\tbake help\n\t\tshows this help message.");
-		$this->out("\n\tbake all <name>\n\t\tbakes complete MVC. optional <name> of a Model");
-		$this->out("\n\tbake project <path>\n\t\tbakes a new app folder in the path supplied\n\t\tor in current directory if no path is specified");
-		$this->out("\n\tbake plugin <name>\n\t\tbakes a new plugin folder in the path supplied\n\t\tor in current directory if no path is specified.");
-		$this->out("\n\tbake db_config\n\t\tbakes a database.php file in config directory.");
-		$this->out("\n\tbake model\n\t\tbakes a model. run 'bake model help' for more info");
-		$this->out("\n\tbake view\n\t\tbakes views. run 'bake view help' for more info");
-		$this->out("\n\tbake controller\n\t\tbakes a controller. run 'bake controller help' for more info");
-		$this->out("\n\tbake fixture\n\t\tbakes fixtures. run 'bake fixture help' for more info.");
-		$this->out("\n\tbake test\n\t\tbakes unit tests. run 'bake test help' for more info.");
-		$this->out();
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+			'The Bake script generates controllers, views and models for your application.' . 
+			'If run with no command line arguments, Bake guides the user through the class' . 
+			'creation process. You can customize the generation process by telling Bake' . 
+			'where different parts of your application are using command line arguments.'
+		)->addSubcommand('all', array(
+			'help' => __('Bake a complete MVC. optional <name> of a Model'),
+		))->addSubcommand('project', array(
+			'help' => __('Bake a new app folder in the path supplied or in current directory if no path is specified'),
+			'parser' => $this->Project->getOptionParser()
+		))->addSubcommand('plugin', array(
+			'help' => __('Bake a new plugin folder in the path supplied or in current directory if no path is specified.'),
+			'parser' => $this->Plugin->getOptionParser()
+		))->addSubcommand('db_config', array(
+			'help' => __('Bake a database.php file in config directory.'),
+			'parser' => $this->DbConfig->getOptionParser()
+		))->addSubcommand('model', array(
+			'help' => __('Bake a model.'),
+			'parser' => $this->Model->getOptionParser()
+		))->addSubcommand('view', array(
+			'help' => __('Bake views for controllers.'),
+			'parser' => $this->View->getOptionParser()
+		))->addSubcommand('controller', array(
+			'help' => __('Bake a controller.'),
+			'parser' => $this->Controller->getOptionParser()
+		))->addSubcommand('fixture', array(
+			'help' => __('Bake a fixture.'),
+			'parser' => $this->Fixture->getOptionParser()
+		))->addSubcommand('test', array(
+			'help' => __('Bake a unit test.'),
+			'parser' => $this->Test->getOptionParser()
+		))->addOption('connection', array(
+			'help' => __('Database connection to use in conjunction with     `bake all`.'),
+			'short' => 'c',
+			'default' => 'default'
+		));
 	}
+
 }
diff --git a/cake/console/shells/command_list.php b/cake/console/shells/command_list.php
new file mode 100644
index 000000000..613f65dd7
--- /dev/null
+++ b/cake/console/shells/command_list.php
@@ -0,0 +1,235 @@
+<?php
+/**
+ * CommandListTest file
+ *
+ * PHP 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2006-2010, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2006-2010, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs
+ * @since         CakePHP v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * Shows a list of commands available from the console.
+ *
+ * @package cake.console.libs
+ */
+class CommandListShell extends Shell {
+
+/**
+ * startup
+ *
+ * @return void
+ */
+	public function startup() {
+		if (empty($this->params['xml'])) {
+			parent::startup();
+		}
+	}
+
+/**
+ * Main function Prints out the list of shells.
+ *
+ * @return void
+ */
+	public function main() {
+		if (empty($this->params['xml'])) {
+			$this->out("<info>Current Paths:</info>", 2);
+			$this->out(" -app: ". APP_DIR);
+			$this->out(" -working: " . rtrim(APP_PATH, DS));
+			$this->out(" -root: " . rtrim(ROOT, DS));
+			$this->out(" -core: " . rtrim(CORE_PATH, DS));
+			$this->out("");
+			$this->out("<info>Changing Paths:</info>", 2);
+			$this->out("Your working path should be the same as your application path");
+			$this->out("to change your path use the '-app' param.");
+			$this->out("Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp", 2);
+
+			$this->out("<info>Available Shells:</info>", 2);
+		}
+
+		$shellList = $this->_getShellList();
+
+		if ($shellList) {
+			ksort($shellList);
+			if (empty($this->params['xml'])) {
+				if (!empty($this->params['sort'])) {
+					$this->_asSorted($shellList);
+				} else {
+					$this->_asText($shellList);
+				}
+			} else {
+				$this->_asXml($shellList);
+			}
+		}
+	}
+
+/**
+ * Gets the shell command listing.
+ *
+ * @return array 
+ */
+	protected function _getShellList() {
+		$shellList = array();
+
+		$corePaths = App::core('shells');
+		$shellList = $this->_appendShells('CORE', $corePaths, $shellList);
+
+		$appPaths = array_diff(App::path('shells'), $corePaths);
+		$shellList = $this->_appendShells('app', $appPaths, $shellList);
+
+		$plugins = App::objects('plugin');
+		foreach ($plugins as $plugin) {
+			$pluginPath = App::pluginPath($plugin) . 'console' . DS . 'shells' . DS;
+			$shellList = $this->_appendShells($plugin, array($pluginPath), $shellList);
+		}
+		return $shellList;
+	}
+
+/**
+ * Scan the provided paths for shells, and append them into $shellList
+ *
+ * @return array
+ */
+	protected function _appendShells($type, $paths, $shellList) {
+		foreach ($paths as $path) {
+			if (!is_dir($path)) {
+				continue;
+			}
+ 			$shells = App::objects('file', $path);
+
+			if (empty($shells)) {
+				continue;
+			}
+			foreach ($shells as $shell) {
+				if ($shell !== 'shell.php') {
+					$shell = str_replace('.php', '', $shell);
+					$shellList[$shell][$type] = $type;
+				}
+			}
+		}
+		return $shellList;
+	}
+
+/**
+ * Output text.
+ *
+ * @return void
+ */
+	protected function _asText($shellList) {
+		if (DS === '/') {
+			$width = exec('tput cols') - 2;
+		}
+		if (empty($width)) {
+			$width = 80;
+		}
+		$columns = max(1, floor($width / 30));
+		$rows = ceil(count($shellList) / $columns);
+
+		foreach ($shellList as $shell => $types) {
+			sort($types);
+			$shellList[$shell] = str_pad($shell . ' [' . implode ($types, ', ') . ']', $width / $columns);
+		}
+		$out = array_chunk($shellList, $rows);
+		for ($i = 0; $i < $rows; $i++) {
+			$row = '';
+			for ($j = 0; $j < $columns; $j++) {
+				if (!isset($out[$j][$i])) {
+					continue;
+				}
+				$row .= $out[$j][$i];
+			}
+			$this->out(" " . $row);
+		}
+		$this->out();
+		$this->out("To run a command, type <info>cake shell_name [args]</info>");
+		$this->out("To get help on a specific command, type <info>cake shell_name --help</info>", 2);
+	}
+
+/**
+ * Generates the shell list sorted by where the shells are found.
+ *
+ * @return void
+ */
+	protected function _asSorted($shellList) {
+		$grouped = array();
+		foreach ($shellList as $shell => $types) {
+			foreach ($types as $type) {
+				$type = Inflector::camelize($type);
+				if (empty($grouped[$type])) {
+					$grouped[$type] = array();
+				}
+				$grouped[$type][] = $shell;
+			}
+		}
+		if (!empty($grouped['App'])) {
+			sort($grouped['App'], SORT_STRING);
+			$this->out('[ App ]');
+			$this->out('  ' . implode(', ', $grouped['App']), 2);
+			unset($grouped['App']);
+		}
+		foreach ($grouped as $section => $shells) {
+			if ($section == 'CORE') {
+				continue;
+			}
+			sort($shells, SORT_STRING);
+			$this->out('[ ' . $section . ' ]');
+			$this->out('  ' . implode(', ', $shells), 2);
+		}
+		if (!empty($grouped['CORE'])) {
+			sort($grouped['CORE'], SORT_STRING);
+			$this->out('[ Core ]');
+			$this->out('  ' . implode(', ', $grouped['CORE']), 2);
+		}
+		$this->out();
+	}
+
+/**
+ * Output as XML
+ *
+ * @return void
+ */
+	protected function _asXml($shellList) {
+		$plugins = App::objects('plugin');
+		$shells = new SimpleXmlElement('<shells></shells>');
+		foreach ($shellList as $name => $location) {
+			$source = current($location);
+			$callable = $name;
+			if (in_array($source, $plugins)) {
+				$callable = Inflector::underscore($source) . '.' . $name;
+			}
+			$shell = $shells->addChild('shell');
+			$shell->addAttribute('name', $name);
+			$shell->addAttribute('call_as', $callable);
+			$shell->addAttribute('provider', $source);
+			$shell->addAttribute('help', $callable . ' -h');
+		}
+		$this->out($shells->saveXml());
+	}
+
+/**
+ * get the option parser
+ *
+ * @return void
+ */
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description('Get the list of available shells for this CakePHP application.')
+			->addOption('xml', array(
+				'help' => __('Get the listing as XML.'),
+				'boolean' => true
+			))->addOption('sort', array(
+				'help' => __('Sorts the commands by where they are located.'),
+				'boolean' => true
+			));
+	}
+}
diff --git a/cake/console/libs/console.php b/cake/console/shells/console.php
similarity index 100%
rename from cake/console/libs/console.php
rename to cake/console/shells/console.php
diff --git a/cake/console/libs/i18n.php b/cake/console/shells/i18n.php
similarity index 77%
rename from cake/console/libs/i18n.php
rename to cake/console/shells/i18n.php
index a9d2888a4..c40160e54 100644
--- a/cake/console/libs/i18n.php
+++ b/cake/console/shells/i18n.php
@@ -65,7 +65,7 @@ class I18nShell extends Shell {
  *
  */
 	public function main() {
-		$this->out(__('I18n Shell'));
+		$this->out(__('<info>I18n Shell</info>'));
 		$this->hr();
 		$this->out(__('[E]xtract POT file from sources'));
 		$this->out(__('[I]nitialize i18n database table'));
@@ -81,7 +81,7 @@ class I18nShell extends Shell {
 				$this->initdb();
 			break;
 			case 'h':
-				$this->help();
+				$this->out($this->OptionParser->help());
 			break;
 			case 'q':
 				exit(0);
@@ -98,27 +98,23 @@ class I18nShell extends Shell {
  *
  */
 	public function initdb() {
-		$this->Dispatch->args = array('schema', 'create', 'i18n');
-		$this->Dispatch->dispatch();
+		$this->dispatchShell('schema create i18n');
 	}
 
 /**
- * Show help screen.
+ * Get and configure the Option parser
  *
+ * @return ConsoleOptionParser
  */
-	public function help() {
-		$this->hr();
-		$this->out(__('I18n Shell:'));
-		$this->hr();
-		$this->out(__('I18n Shell initializes i18n database table for your application'));
-		$this->out(__('and generates .pot file(s) with translations.'));
-		$this->hr();
-		$this->out(__('usage:'));
-		$this->out('   cake i18n help');
-		$this->out('   cake i18n initdb [-datasource custom]');
-		$this->out();
-		$this->hr();
-
-		$this->Extract->help();
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+			__('I18n Shell initializes i18n database table for your application and generates .pot files(s) with translations.')
+			)->addSubcommand('initdb', array(
+				'help' => __('Initialize the i18n table.')
+			))->addSubcommand('extract', array(
+				'help' => __('Extract the po translations from your application'),
+				'parser' => $this->Extract->getOptionParser()
+			));
 	}
 }
diff --git a/cake/console/libs/schema.php b/cake/console/shells/schema.php
similarity index 75%
rename from cake/console/libs/schema.php
rename to cake/console/shells/schema.php
index 45e7795d9..612e57e45 100644
--- a/cake/console/libs/schema.php
+++ b/cake/console/shells/schema.php
@@ -40,6 +40,13 @@ class SchemaShell extends Shell {
  */
 	private $__dry = null;
 
+/**
+ * Schema class being used.
+ *
+ * @var CakeSchema
+ */
+	public $Schema;
+
 /**
  * Override initialize
  *
@@ -95,14 +102,6 @@ class SchemaShell extends Shell {
 		$this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin'));
 	}
 
-/**
- * Override main
- *
- */
-	public function main() {
-		$this->help();
-	}
-
 /**
  * Read and output contents of schema object
  * path to read as second arg
@@ -128,7 +127,7 @@ class SchemaShell extends Shell {
 	public function generate() {
 		$this->out(__('Generating Schema...'));
 		$options = array();
-		if (isset($this->params['f'])) {
+		if (isset($this->params['force'])) {
 			$options = array('models' => false);
 		}
 
@@ -156,8 +155,8 @@ class SchemaShell extends Shell {
 			$result = $Folder->read();
 
 			$numToUse = false;
-			if (isset($this->params['s'])) {
-				$numToUse = $this->params['s'];
+			if (isset($this->params['snapshot'])) {
+				$numToUse = $this->params['snapshot'];
 			}
 
 			$count = 0;
@@ -203,7 +202,7 @@ class SchemaShell extends Shell {
 			$this->err(__('Schema could not be loaded'));
 			$this->_stop();
 		}
-		if (isset($this->params['write'])) {
+		if (!empty($this->params['write'])) {
 			if ($this->params['write'] == 1) {
 				$write = Inflector::underscore($this->Schema->name);
 			} else {
@@ -263,22 +262,22 @@ class SchemaShell extends Shell {
  */
 	function _loadSchema() {
 		$name = $plugin = null;
-		if (isset($this->params['name'])) {
+		if (!empty($this->params['name'])) {
 			$name = $this->params['name'];
 		}
-		if (isset($this->params['plugin'])) {
+		if (!empty($this->params['plugin'])) {
 			$plugin = $this->params['plugin'];
 		}
 		
-		if (isset($this->params['dry'])) {
+		if (!empty($this->params['dry'])) {
 			$this->__dry = true;
 			$this->out(__('Performing a dry run.'));
 		}
 
 		$options = array('name' => $name, 'plugin' => $plugin);
-		if (isset($this->params['s'])) {
+		if (!empty($this->params['snapshot'])) {
 			$fileName = rtrim($this->Schema->file, '.php');
-			$options['file'] = $fileName . '_' . $this->params['s'] . '.php';
+			$options['file'] = $fileName . '_' . $this->params['snapshot'] . '.php';
 		}
 
 		$Schema =& $this->Schema->load($options);
@@ -348,7 +347,7 @@ class SchemaShell extends Shell {
 
 		$this->out(__('Comparing Database to Schema...'));
 		$options = array();
-		if (isset($this->params['f'])) {
+		if (isset($this->params['force'])) {
 			$options['models'] = false;
 		}
 		$Old = $this->Schema->read($options);
@@ -422,78 +421,97 @@ class SchemaShell extends Shell {
 	}
 
 /**
- * Displays help contents
+ * get the option parser
  *
+ * @return void
  */
-	public function help() {
-		$help = <<<TEXT
-The Schema Shell generates a schema object from
-the database and updates the database from the schema.
----------------------------------------------------------------
-Usage: cake schema <command> <arg1> <arg2>...
----------------------------------------------------------------
-Params:
-	-connection <config>
-		set db config <config>. uses 'default' if none is specified
-
-	-path <dir>
-		path <dir> to read and write schema.php.
-		default path: {$this->Schema->path}
-
-	-name <name>
-		Classname to use. If <name> is Plugin.className, it will
-		set the plugin and name params.
-
-	-file <name>
-		file <name> to read and write.
-		default file: {$this->Schema->file}
-
-	-s <number>
-		snapshot <number> to use for run.
-
-	-dry
-		Perform a dry run on create + update commands.
-		Queries will be output to window instead of executed.
-
-	-f
-		force 'generate' to create a new schema.
-
-	-plugin
-		Indicate the plugin to use.
-
-Commands:
-
-	schema help
-		shows this help message.
-
-	schema view <name>
-		read and output contents of schema file.
-
-	schema generate
-		reads from 'connection' writes to 'path'
-		To force generation of all tables into the schema, use the -f param.
-		Use 'schema generate snapshot <number>' to generate snapshots
-		which you can use with the -s parameter in the other operations.
-
-	schema dump <name>
-		Dump database sql based on schema file to stdout.
-		If you use the `-write` param is used a .sql will be generated.
-		If `-write` is a filename, then that file name will be generate.
-		If `-write` is a full path, the schema will be written there.
-
-	schema create <name> <table>
-		Drop and create tables based on schema file
-		optional <table> argument can be used to create only a single 
-		table in the schema. Pass the -s param with a number to use a snapshot.
-		Use the `-dry` param to preview the changes.
-
-	schema update <name> <table>
-		Alter the tables based on schema file. Optional <table>
-		parameter will only update one table. 
-		To use a snapshot pass the `-s` param with the snapshot number.
-		To preview the changes that will be done use `-dry`.
-TEXT;
-		$this->out($help);
-		$this->_stop();
+	public function getOptionParser() {
+		$plugin = array(
+			'help' => __('The plugin to use.'),
+		);
+		$connection = array(
+			'help' => __('Set the db config to use.'),
+			'default' => 'default'
+		);
+		$path = array(
+			'help' => __('Path to read and write schema.php'),
+			'default' => CONFIGS . 'schema'
+		);
+		$file = array(
+			'help' => __('File name to read and write.'),
+			'default' => 'schema.php'
+		);
+		$name = array(
+			'help' => __('Classname to use. If its Plugin.class, both name and plugin options will be set.')
+		);
+		$snapshot = array(
+			'short' => 's',
+			'help' => __('Snapshot number to use/make.')
+		);
+		$dry = array(
+			'help' => 'Perform a dry run on create and update commands. Queries will be output instead of run.',
+			'boolean' => true
+		);
+		$force = array(
+			'short' => 'f',
+			'help' => __('Force "generate" to create a new schema'),
+			'boolean' => true
+		);
+		$write = array(
+			'help' => __('Write the dumped SQL to a file.')
+		);
+		
+		$parser = parent::getOptionParser();
+		$parser->description(
+			'The Schema Shell generates a schema object from' .
+			'the database and updates the database from the schema.'
+		)->addSubcommand('view', array(
+			'help' => 'read and output the contents of a schema file',
+			'parser' => array(
+				'options' => compact('plugin', 'path', 'file', 'name', 'connection'),
+				'arguments' => compact('name')
+			)
+		))->addSubcommand('generate', array(
+			'help' => __('Reads from --connection and writes to --path. Generate snapshots with -s'),
+			'parser' => array(
+				'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'snapshot', 'force'),
+				'arguments' => array(
+					'snapshot' => array('help' => __('Generate a snapshot.'))
+				)
+			)
+		))->addSubcommand('dump', array(
+			'help' => __('Dump database SQL based on a schema file to stdout.'),
+			'parser' => array(
+				'options' => compact('plugin', 'path', 'file', 'name', 'connection'),
+				'arguments' => compact('name')
+			)
+		))->addSubcommand('create', array(
+			'help' => __('Drop and create tables based on the schema file.'),
+			'parser' => array(
+				'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot'),
+				'args' => array(
+					'name' => array(
+						'help' => __('Name of schema to use.')
+					),
+					'table' => array(
+						'help' => __('Only create the specified table.')
+					)
+				)
+			)
+		))->addSubcommand('update', array(
+			'help' => __('Alter the tables based on the schema file.'),
+			'parser' => array(
+				'options' => compact('plugin', 'path', 'file', 'name', 'connection', 'dry', 'snapshot'),
+				'args' => array(
+					'name' => array(
+						'help' => __('Name of schema to use.')
+					),
+					'table' => array(
+						'help' => __('Only create the specified table.')
+					)
+				)
+			)
+		));
+		return $parser;
 	}
 }
diff --git a/cake/console/libs/shell.php b/cake/console/shells/shell.php
similarity index 53%
rename from cake/console/libs/shell.php
rename to cake/console/shells/shell.php
index 85b15946c..8ce538105 100644
--- a/cake/console/libs/shell.php
+++ b/cake/console/shells/shell.php
@@ -17,7 +17,10 @@
  * @since         CakePHP(tm) v 1.2.0.5012
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-App::import('Shell', 'TaskCollection');
+require_once CONSOLE_LIBS . 'task_collection.php';
+require_once CONSOLE_LIBS . 'console_output.php';
+require_once CONSOLE_LIBS . 'console_input.php';
+require_once CONSOLE_LIBS . 'console_option_parser.php';
 
 /**
  * Base class for command-line utilities for automating programmer chores.
@@ -28,12 +31,18 @@ App::import('Shell', 'TaskCollection');
 class Shell extends Object {
 
 /**
- * An instance of the ShellDispatcher object that loaded this script
- *
- * @var ShellDispatcher
- * @access public
+ * Output constants for making verbose and quiet shells.
  */
-	public $Dispatch = null;
+	const VERBOSE = 2;
+	const NORMAL = 1;
+	const QUIET = 0;
+
+/**
+ * An instance of ConsoleOptionParser that has been configured for this class.
+ *
+ * @var ConsoleOptionParser
+ */
+	public $OptionParser;
 
 /**
  * If true, the script will ask for permission to perform actions.
@@ -43,15 +52,6 @@ class Shell extends Object {
  */
 	public $interactive = true;
 
-/**
- * Holds the DATABASE_CONFIG object for the app. Null if database.php could not be found,
- * or the app does not exist.
- *
- * @var DATABASE_CONFIG
- * @access public
- */
-	public $DbConfig = null;
-
 /**
  * Contains command switches parsed from the command line.
  *
@@ -60,6 +60,13 @@ class Shell extends Object {
  */
 	public $params = array();
 
+/**
+ * The command (method/task) that is being run.
+ *
+ * @var string
+ */
+	public $command;
+
 /**
  * Contains arguments parsed from the command line.
  *
@@ -68,30 +75,6 @@ class Shell extends Object {
  */
 	public $args = array();
 
-/**
- * The file name of the shell that was invoked.
- *
- * @var string
- * @access public
- */
-	public $shell = null;
-
-/**
- * The class name of the shell that was invoked.
- *
- * @var string
- * @access public
- */
-	public $className = null;
-
-/**
- * The command called if public methods are available.
- *
- * @var string
- * @access public
- */
-	public $command = null;
-
 /**
  * The name of the shell in camelized.
  *
@@ -100,14 +83,6 @@ class Shell extends Object {
  */
 	public $name = null;
 
-/**
- * An alias for the shell
- *
- * @var string
- * @access public
- */
-	public $alias = null;
-
 /**
  * Contains tasks to load and instantiate
  *
@@ -139,30 +114,56 @@ class Shell extends Object {
  */
 	public $Tasks;
 
+/**
+ * Normalized map of tasks.
+ *
+ * @var string
+ */
+	protected $_taskMap = array();
+
+/**
+ * stdout object.
+ *
+ * @var ConsoleOutput
+ */
+	public $stdout;
+
+/**
+ * stderr object.
+ *
+ * @var ConsoleOutput
+ */
+	public $stderr;
+
+/**
+ * stdin object
+ *
+ * @var ConsoleInput
+ */
+	public $stdin;
+
 /**
  *  Constructs this Shell instance.
  *
  */
-	function __construct(&$dispatch) {
-		$vars = array('params', 'args', 'shell', 'shellCommand' => 'command');
-
-		foreach ($vars as $key => $var) {
-			if (is_string($key)) {
-				$this->{$var} =& $dispatch->{$key};
-			} else {
-				$this->{$var} =& $dispatch->{$var};
-			}
-		}
-
+	function __construct($stdout = null, $stderr = null, $stdin = null) {
 		if ($this->name == null) {
-			$this->name = get_class($this);
+			$this->name = Inflector::underscore(str_replace(array('Shell', 'Task'), '', get_class($this)));
 		}
+		$this->Tasks = new TaskCollection($this);
 
-		if ($this->alias == null) {
-			$this->alias = $this->name;
+		$this->stdout = $stdout;
+		$this->stderr = $stderr;
+		$this->stdin = $stdin;
+		if ($this->stdout == null) {
+			$this->stdout = new ConsoleOutput('php://stdout');
+		}
+		if ($this->stderr == null) {
+			$this->stderr = new ConsoleOutput('php://stderr');
+		}
+		if ($this->stdin == null) {
+			$this->stdin = new ConsoleInput('php://stdin');
 		}
-		$this->Dispatch =& $dispatch;
-		$this->Tasks = $dispatch->getTaskCollection();
 	}
 
 /**
@@ -190,31 +191,15 @@ class Shell extends Object {
  *
  */
 	protected function _welcome() {
-		$this->Dispatch->clear();
+		$this->clear();
 		$this->out();
-		$this->out('Welcome to CakePHP v' . Configure::version() . ' Console');
+		$this->out('<info>Welcome to CakePHP v' . Configure::version() . ' Console</info>');
 		$this->hr();
-		$this->out('App : '. $this->params['app']);
-		$this->out('Path: '. $this->params['working']);
+		$this->out('App : '. APP_DIR);
+		$this->out('Path: '. APP_PATH);
 		$this->hr();
 	}
 
-/**
- * Loads database file and constructs DATABASE_CONFIG class
- * makes $this->DbConfig available to subclasses
- *
- * @return bool
- */
-	protected function _loadDbConfig() {
-		if (config('database') && class_exists('DATABASE_CONFIG')) {
-			$this->DbConfig =& new DATABASE_CONFIG();
-			return true;
-		}
-		$this->err('Database config could not be loaded.');
-		$this->out('Run `bake` to create the database configuration.');
-		return false;
-	}
-
 /**
  * if public $uses = true
  * Loads AppModel file and constructs AppModel class
@@ -228,11 +213,6 @@ class Shell extends Object {
 			return;
 		}
 
-		if ($this->uses === true && App::import('Model', 'AppModel')) {
-			$this->AppModel =& new AppModel(false, false, false);
-			return true;
-		}
-
 		if ($this->uses !== true && !empty($this->uses)) {
 			$uses = is_array($this->uses) ? $this->uses : array($this->uses);
 
@@ -260,14 +240,159 @@ class Shell extends Object {
 		if ($this->tasks === true || empty($this->tasks) || empty($this->Tasks)) {
 			return true;
 		}
-		$tasks = TaskCollection::normalizeObjectArray((array)$this->tasks);
-		foreach ($tasks as $task => $properties) {
-			$this->{$task} = $this->Tasks->load($properties['class'], $properties['settings']);
+		$this->_taskMap = TaskCollection::normalizeObjectArray((array)$this->tasks);
+		foreach ($this->_taskMap as $task => $properties) {
 			$this->taskNames[] = $task;
 		}
 		return true;
 	}
 
+/**
+ * Check to see if this shell has a task with the provided name.
+ *
+ * @param string $task The task name to check.
+ * @return boolean Success
+ */
+	public function hasTask($task) {
+		return isset($this->_taskMap[Inflector::camelize($task)]);
+	}
+
+/**
+ * Check to see if this shell has a callable method by the given name.
+ *
+ * @param string $name The method name to check.
+ * @return boolean
+ */
+	public function hasMethod($name) {
+		if (empty($this->_reflection)) {
+			$this->_reflection = new ReflectionClass($this);
+		}
+		try {
+			$method = $this->_reflection->getMethod($name);
+			if (!$method->isPublic() || substr($name, 0, 1) === '_') {
+				return false;
+			}
+			if ($method->getDeclaringClass() != $this->_reflection) {
+				return false;
+			}
+			return true;
+		} catch (ReflectionException $e) {
+			return false;
+		}
+	}
+
+/**
+ * Dispatch a command to another Shell. Similar to Object::requestAction()
+ * but intended for running shells from other shells.
+ *
+ * ### Usage:
+ * 
+ * With a string commmand:
+ *
+ *	`return $this->dispatchShell('schema create DbAcl');`
+ *
+ * With an array command:
+ *
+ * `return $this->dispatchShell('schema', 'create', 'i18n', '--dry');` 
+ *
+ * @param mixed $command Either an array of args similar to $argv. Or a string command, that can be 
+ *   exploded on space to simulate argv.
+ * @return mixed. The return of the other shell.
+ */
+	public function dispatchShell() {
+		$args = func_get_args();
+		if (is_string($args[0]) && count($args) == 1) {
+			$args = explode(' ', $args[0]);
+		}
+
+		$Dispatcher = new ShellDispatcher($args, false);
+		return $Dispatcher->dispatch();
+	}
+
+/**
+ * Runs the Shell with the provided argv
+ *
+ * @param array $argv Array of arguments to run the shell with. This array should be missing the shell name.
+ * @return void
+ */
+	public function runCommand($command, $argv) {
+		$isTask = $this->hasTask($command);
+		$isMethod = $this->hasMethod($command);
+		$isMain = $this->hasMethod('main');
+
+		if ($isTask || $isMethod && $command !== 'execute') {
+			array_shift($argv);
+		}
+
+		$this->OptionParser = $this->getOptionParser();
+		list($this->params, $this->args) = $this->OptionParser->parse($argv, $command);
+		$this->command = $command;
+
+		if (!empty($this->params['help'])) {
+			return $this->_displayHelp($command);
+		}
+
+		if (($isTask || $isMethod || $isMain) && $command !== 'execute' ) {
+			$this->startup();
+		}
+
+		if ($isTask) {
+			$command = Inflector::camelize($command);
+			return $this->{$command}->runCommand('execute', $argv);
+		}
+		if ($isMethod) {
+			return $this->{$command}();
+		}
+		if ($isMain) {
+			return $this->main();
+		}
+		return $this->out($this->OptionParser->help($command));
+	}
+
+/**
+ * Display the help in the correct format
+ *
+ * @return void
+ */
+	protected function _displayHelp($command) {
+		$format = 'text';
+		if (!empty($this->args[0]) && $this->args[0] == 'xml')  {
+			$format = 'xml';
+			$this->output->outputAs(ConsoleOutput::RAW);
+		} else {
+			$this->_welcome();
+		}
+		return $this->out($this->OptionParser->help($command, $format));
+	}
+
+/**
+ * Gets the option parser instance and configures it.
+ * By overriding this method you can configure the ConsoleOptionParser before returning it.
+ *
+ * @return ConsoleOptionParser
+ */
+	public function getOptionParser() {
+		$parser = new ConsoleOptionParser($this->name);
+		return $parser;
+	}
+
+/**
+ * Overload get for lazy building of tasks
+ *
+ * @return void
+ */
+	public function __get($name) {
+		if (empty($this->{$name}) && in_array($name, $this->taskNames)) {
+			$properties = $this->_taskMap[$name];
+			$this->{$name} = $this->Tasks->load($properties['class'], $properties['settings']);
+			$this->{$name}->args =& $this->args;
+			$this->{$name}->params =& $this->params;
+			$this->{$name}->initialize();
+			$this->{$name}->loadTasks();
+		}
+		return $this->{$name};
+	}
+
 /**
  * Prompts the user for input, and returns it.
  *
@@ -280,7 +405,7 @@ class Shell extends Object {
 		if (!$this->interactive) {
 			return $default;
 		}
-		$in = $this->Dispatch->getInput($prompt, $options, $default);
+		$in = $this->_getInput($prompt, $options, $default);
 
 		if ($options && is_string($options)) {
 			if (strpos($options, ',')) {
@@ -293,7 +418,7 @@ class Shell extends Object {
 		}
 		if (is_array($options)) {
 			while ($in == '' || ($in && (!in_array(strtolower($in), $options) && !in_array(strtoupper($in), $options)) && !in_array($in, $options))) {
-				$in = $this->Dispatch->getInput($prompt, $options, $default);
+				$in = $this->_getInput($prompt, $options, $default);
 			}
 		}
 		if ($in) {
@@ -301,19 +426,86 @@ class Shell extends Object {
 		}
 	}
 
+/**
+ * Prompts the user for input, and returns it.
+ *
+ * @param string $prompt Prompt text.
+ * @param mixed $options Array or string of options.
+ * @param string $default Default input value.
+ * @return Either the default value, or the user-provided input.
+ */
+	protected function _getInput($prompt, $options, $default) {
+		if (!is_array($options)) {
+			$printOptions = '';
+		} else {
+			$printOptions = '(' . implode('/', $options) . ')';
+		}
+
+		if ($default === null) {
+			$this->stdout->write('<question>' . $prompt . '</question>' . " $printOptions \n" . '> ', 0);
+		} else {
+			$this->stdout->write('<question>' . $prompt . '</question>' . " $printOptions \n" . "[$default] > ", 0);
+		}
+		$result = $this->stdin->read();
+
+		if ($result === false) {
+			$this->_stop(1);
+		}
+		$result = trim($result);
+
+		if ($default != null && empty($result)) {
+			return $default;
+		}
+		return $result;
+	}
+
+/**
+ * Wrap a block of text.
+ * Allows you to set the width, and indenting on a block of text. 
+ *
+ * ### Options
+ *
+ * - `width` The width to wrap to.  Defaults to 72
+ * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
+ * - `indent` Indent the text with the string provided. Defaults to null.
+ *
+ * @param string $text Text the text to format.
+ * @param mixed $options Array of options to use, or an integer to wrap the text to. 
+ * @return string Wrapped / indented text
+ * @see String::wrap()
+ */
+	public function wrapText($text, $options = array()) {
+		return String::wrap($text, $options);
+	}
+
 /**
  * Outputs a single or multiple messages to stdout. If no parameters
  * are passed outputs just a newline.
  *
+ * ### Output levels
+ *
+ * There are 3 built-in output level.  Shell::QUIET, Shell::NORMAL, Shell::VERBOSE.
+ * The verbose and quiet output levels, map to the `verbose` and `quiet` output switches
+ * present in  most shells.  Using Shell::QUIET for a message means it will always display.
+ * While using Shell::VERBOSE means it will only display when verbose output is toggled.
+ *
  * @param mixed $message A string or a an array of strings to output
  * @param integer $newlines Number of newlines to append
+ * @param integer $level The message's output level, see above.
  * @return integer Returns the number of bytes returned from writing to stdout.
  */
-	public function out($message = null, $newlines = 1) {
-		if (is_array($message)) {
-			$message = implode($this->nl(), $message);
+	public function out($message = null, $newlines = 1, $level = Shell::NORMAL) {
+		$currentLevel = Shell::NORMAL;
+		if (!empty($this->params['verbose'])) {
+			$currentLevel = Shell::VERBOSE;
 		}
-		return $this->Dispatch->stdout($message . $this->nl($newlines), false);
+		if (!empty($this->params['quiet'])) {
+			$currentLevel = Shell::QUIET;
+		}
+		if ($level <= $currentLevel) {
+			return $this->stdout->write($message, $newlines);
+		}
+		return true;
 	}
 
 /**
@@ -324,10 +516,7 @@ class Shell extends Object {
  * @param integer $newlines Number of newlines to append
  */
 	public function err($message = null, $newlines = 1) {
-		if (is_array($message)) {
-			$message = implode($this->nl(), $message);
-		}
-		$this->Dispatch->stderr($message . $this->nl($newlines));
+		$this->stderr->write($message, $newlines);
 	}
 
 /**
@@ -338,17 +527,18 @@ class Shell extends Object {
  * @return string
  */
 	function nl($multiplier = 1) {
-		return str_repeat("\n", $multiplier);
+		return str_repeat(ConsoleOutput::LF, $multiplier);
 	}
 
 /**
  * Outputs a series of minus characters to the standard output, acts as a visual separator.
  *
  * @param integer $newlines Number of newlines to pre- and append
+ * @param integer $width Width of the line, defaults to 63
  */
-	public function hr($newlines = 0) {
+	public function hr($newlines = 0, $width = 63) {
 		$this->out(null, $newlines);
-		$this->out('---------------------------------------------------------------');
+		$this->out(str_repeat('-', $width));
 		$this->out(null, $newlines);
 	}
 
@@ -360,7 +550,7 @@ class Shell extends Object {
  * @param string $message An optional error message
  */
 	public function error($title, $message = null) {
-		$this->err(sprintf(__('Error: %s'), $title));
+		$this->err(sprintf(__('<error>Error:</error> %s'), $title));
 
 		if (!empty($message)) {
 			$this->err($message);
@@ -369,21 +559,17 @@ class Shell extends Object {
 	}
 
 /**
- * Will check the number args matches otherwise throw an error
+ * Clear the console
  *
- * @param integer $expectedNum Expected number of paramters
- * @param string $command Command
+ * @return void
  */
-	protected function _checkArgs($expectedNum, $command = null) {
-		if (!$command) {
-			$command = $this->command;
-		}
-		if (count($this->args) < $expectedNum) {
-			$message[] = "Got: " . count($this->args);
-			$message[] = "Expected: {$expectedNum}";
-			$message[] = "Please type `cake {$this->shell} help` for help";
-			$message[] = "on usage of the {$this->name} {$command}.";
-			$this->error('Wrong number of parameters', $message);
+	public function clear() {
+		if (empty($this->params['noclear'])) {
+			if ( DS === '/') {
+				passthru('clear');
+			} else {
+				passthru('cls');
+			}
 		}
 	}
 
@@ -398,20 +584,22 @@ class Shell extends Object {
 		$path = str_replace(DS . DS, DS, $path);
 
 		$this->out();
-		$this->out(sprintf(__('Creating file %s'), $path));
 
 		if (is_file($path) && $this->interactive === true) {
-			$prompt = sprintf(__('File `%s` exists, overwrite?'), $path);
-			$key = $this->in($prompt,  array('y', 'n', 'q'), 'n');
+			$this->out(sprintf(__('<warning>File `%s` exists</warning>'), $path));
+			$key = $this->in(__('Do you want to overwrite?'),  array('y', 'n', 'q'), 'n');
 
 			if (strtolower($key) == 'q') {
-				$this->out(__('Quitting.'), 2);
+				$this->out(__('<error>Quitting</error>.'), 2);
 				$this->_stop();
 			} elseif (strtolower($key) != 'y') {
 				$this->out(sprintf(__('Skip `%s`'), $path), 2);
 				return false;
 			}
+		} else {
+			$this->out(sprintf(__('Creating file %s'), $path));
 		}
+
 		if (!class_exists('File')) {
 			require LIBS . 'file.php';
 		}
@@ -419,27 +607,14 @@ class Shell extends Object {
 		if ($File = new File($path, true)) {
 			$data = $File->prepare($contents);
 			$File->write($data);
-			$this->out(sprintf(__('Wrote `%s`'), $path));
+			$this->out(sprintf(__('<success>Wrote</success> `%s`'), $path));
 			return true;
 		} else {
-			$this->err(sprintf(__('Could not write to `%s`.'), $path), 2);
+			$this->err(sprintf(__('<error>Could not write to `%s`</error>.'), $path), 2);
 			return false;
 		}
 	}
 
-/**
- * Outputs usage text on the standard output. Implement it in subclasses.
- *
- */
-	public function help() {
-		if ($this->command != null) {
-			$this->err("Unknown {$this->name} command `{$this->command}`.");
-			$this->err("For usage, try `cake {$this->shell} help`.", 2);
-		} else {
-			$this->Dispatch->help();
-		}
-	}
-
 /**
  * Action to create a Unit Test
  *
@@ -449,13 +624,13 @@ class Shell extends Object {
 		if (App::import('vendor', 'simpletest' . DS . 'simpletest')) {
 			return true;
 		}
-		$prompt = 'SimpleTest is not installed. Do you want to bake unit test files anyway?';
+		$prompt = 'PHPUnit is not installed. Do you want to bake unit test files anyway?';
 		$unitTest = $this->in($prompt, array('y','n'), 'y');
 		$result = strtolower($unitTest) == 'y' || strtolower($unitTest) == 'yes';
 
 		if ($result) {
 			$this->out();
-			$this->out('You can download SimpleTest from http://simpletest.org');
+			$this->out('You can download PHPUnit from http://phpunit.de');
 		}
 		return $result;
 	}
diff --git a/cake/console/libs/tasks/bake.php b/cake/console/shells/tasks/bake.php
similarity index 72%
rename from cake/console/libs/tasks/bake.php
rename to cake/console/shells/tasks/bake.php
index 7da7b820b..6a89e187c 100644
--- a/cake/console/libs/tasks/bake.php
+++ b/cake/console/shells/tasks/bake.php
@@ -51,9 +51,27 @@ class BakeTask extends Shell {
 	public function getPath() {
 		$path = $this->path;
 		if (isset($this->plugin)) {
-			$name = substr($this->name, 0, strlen($this->name) - 4);
-			$path = $this->_pluginPath($this->plugin) . Inflector::pluralize(Inflector::underscore($name)) . DS;
+			$path = $this->_pluginPath($this->plugin) . Inflector::pluralize(Inflector::underscore($this->name)) . DS;
 		}
 		return $path;
 	}
+
+/**
+ * Base execute method parses some parameters and sets some properties on the bake tasks.
+ * call when overriding execute()
+ *
+ * @return void
+ */
+	public function execute() {
+		foreach($this->args as $i => $arg) {
+			if (strpos($arg, '.')) {
+				list($this->params['plugin'], $this->args[$i]) = pluginSplit($arg);
+				break;
+			}
+		}
+		if (isset($this->params['plugin'])) {
+			$this->plugin = $this->params['plugin'];
+		}
+	}
+
 }
diff --git a/cake/console/libs/tasks/controller.php b/cake/console/shells/tasks/controller.php
similarity index 91%
rename from cake/console/libs/tasks/controller.php
rename to cake/console/shells/tasks/controller.php
index 21f53b0df..2d7433936 100644
--- a/cake/console/libs/tasks/controller.php
+++ b/cake/console/shells/tasks/controller.php
@@ -56,6 +56,7 @@ class ControllerTask extends BakeTask {
  *
  */
 	public function execute() {
+		parent::execute();
 		if (empty($this->args)) {
 			return $this->_interactive();
 		}
@@ -69,26 +70,22 @@ class ControllerTask extends BakeTask {
 			}
 
 			$controller = $this->_controllerName($this->args[0]);
-			$actions = 'scaffold';
+			$actions = '';
 
-			if (!empty($this->args[1]) && ($this->args[1] == 'public' || $this->args[1] == 'scaffold')) {
+			if (!empty($this->params['public'])) {
 				$this->out(__('Baking basic crud methods for ') . $controller);
-				$actions = $this->bakeActions($controller);
-			} elseif (!empty($this->args[1]) && $this->args[1] == 'admin') {
-				$admin = $this->Project->getPrefix();
-				if ($admin) {
-					$this->out(sprintf(__('Adding %s methods'), $admin));
-					$actions = $this->bakeActions($controller, $admin);
-				}
+				$actions .= $this->bakeActions($controller);
 			}
-
-			if (!empty($this->args[2]) && $this->args[2] == 'admin') {
+			if (!empty($this->params['admin'])) {
 				$admin = $this->Project->getPrefix();
 				if ($admin) {
 					$this->out(sprintf(__('Adding %s methods'), $admin));
 					$actions .= "\n" . $this->bakeActions($controller, $admin);
 				}
 			}
+			if (empty($actions)) {
+				$actions = 'scaffold';
+			}
 
 			if ($this->bake($controller, $actions)) {
 				if ($this->_checkUnitTest()) {
@@ -431,6 +428,34 @@ class ControllerTask extends BakeTask {
 		return $controllerName;
 	}
 
+/**
+ * get the option parser.
+ *
+ * @return void
+ */
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+				__('Bake a controller for a model. Using options you can bake public, admin or both.')
+			)->addArgument('name', array(
+				'help' => __('Name of the controller to bake. Can use Plugin.name to bake controllers into plugins.')
+			))->addOption('public', array(
+				'help' => __('Bake a controller with basic crud actions (index, view, add, edit, delete).'),
+				'boolean' => true
+			))->addOption('admin', array(
+				'help' => __('Bake a controller with crud actions for one of the Routing.prefixes.'),
+				'boolean' => true
+			))->addOption('plugin', array(
+				'short' => 'p',
+				'help' => __('Plugin to bake the controller into.')
+			))->addOption('connection', array(
+				'short' => 'c',
+				'help' => __('The connection the controller\'s model is on.')
+			))->addSubcommand('all', array(
+				'help' => __('Bake all controllers with CRUD methods.')
+			))->epilog(__('Omitting all arguments and options will enter into an interactive mode.'));
+	}
+
 /**
  * Displays help contents
  *
diff --git a/cake/console/libs/tasks/db_config.php b/cake/console/shells/tasks/db_config.php
similarity index 92%
rename from cake/console/libs/tasks/db_config.php
rename to cake/console/shells/tasks/db_config.php
index c62cbe1f3..d7e5a20c0 100644
--- a/cake/console/libs/tasks/db_config.php
+++ b/cake/console/shells/tasks/db_config.php
@@ -30,7 +30,6 @@ class DbConfigTask extends Shell {
  * path to CONFIG directory
  *
  * @var string
- * @access public
  */
 	public $path = null;
 
@@ -38,12 +37,19 @@ class DbConfigTask extends Shell {
  * Default configuration settings to use
  *
  * @var array
- * @access private
  */
 	protected $_defaultConfig = array(
-		'name' => 'default', 'driver'=> 'mysql', 'persistent'=> 'false', 'host'=> 'localhost',
-		'login'=> 'root', 'password'=> 'password', 'database'=> 'project_name',
-		'schema'=> null, 'prefix'=> null, 'encoding' => null, 'port' => null
+		'name' => 'default',
+		'driver'=> 'mysql',
+		'persistent'=> 'false',
+		'host'=> 'localhost',
+		'login'=> 'root',
+		'password'=> 'password',
+		'database'=> 'project_name',
+		'schema'=> null,
+		'prefix'=> null,
+		'encoding' => null,
+		'port' => null
 	);
 
 /**
@@ -60,7 +66,7 @@ class DbConfigTask extends Shell {
  * @var string
  */
 	public function initialize() {
-		$this->path = $this->params['working'] . DS . 'config' . DS;
+		$this->path = APP . 'config' . DS;
 	}
 
 /**
@@ -305,10 +311,10 @@ class DbConfigTask extends Shell {
 		$out .= "class DATABASE_CONFIG {\n\n";
 
 		foreach ($configs as $config) {
-			$config = array_merge($this->__defaultConfig, $config);
+			$config = array_merge($this->_defaultConfig, $config);
 			extract($config);
 
-			$out .= "\tvar \${$name} = array(\n";
+			$out .= "\tpublic \${$name} = array(\n";
 			$out .= "\t\t'driver' => '{$driver}',\n";
 			$out .= "\t\t'persistent' => {$persistent},\n";
 			$out .= "\t\t'host' => '{$host}',\n";
@@ -337,7 +343,6 @@ class DbConfigTask extends Shell {
 		}
 
 		$out .= "}\n";
-		$out .= "?>";
 		$filename = $this->path . 'database.php';
 		return $this->createFile($filename, $out);
 	}
@@ -362,4 +367,16 @@ class DbConfigTask extends Shell {
 		}
 		return $useDbConfig;
 	}
+
+/**
+ * get the option parser
+ *
+ * @return ConsoleOptionParser
+ */
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+				__('Bake new database configuration settings.')
+			);
+	}
 }
diff --git a/cake/console/libs/tasks/extract.php b/cake/console/shells/tasks/extract.php
similarity index 89%
rename from cake/console/libs/tasks/extract.php
rename to cake/console/shells/tasks/extract.php
index a03790cad..78a95fb3d 100644
--- a/cake/console/libs/tasks/extract.php
+++ b/cake/console/shells/tasks/extract.php
@@ -90,6 +90,13 @@ class ExtractTask extends Shell {
  */
 	private $__output = null;
 
+/**
+ * An array of directories to exclude.
+ *
+ * @var array
+ */
+	protected $_exclude = array();
+
 /**
  * Execution method always used for tasks
  *
@@ -97,14 +104,17 @@ class ExtractTask extends Shell {
  * @access private
  */
 	function execute() {
+		if (!empty($this->params['exclude'])) {
+			$this->_exclude = explode(',', $this->params['exclude']);
+		}
 		if (isset($this->params['files']) && !is_array($this->params['files'])) {
 			$this->__files = explode(',', $this->params['files']);
 		}
 		if (isset($this->params['paths'])) {
 			$this->__paths = explode(',', $this->params['paths']);
 		} else {
-			$defaultPath = $this->params['working'];
-			$message = sprintf(__("What is the full path you would like to extract?\nExample: %s\n[Q]uit [D]one"), $this->params['root'] . DS . 'myapp');
+			$defaultPath = APP_PATH;
+			$message = sprintf(__("What is the full path you would like to extract?\nExample: %s\n[Q]uit [D]one"), $this->Dispatch->params['root'] . DS . 'myapp');
 			while (true) {
 				$response = $this->in($message, null, $defaultPath);
 				if (strtoupper($response) === 'Q') {
@@ -182,6 +192,27 @@ class ExtractTask extends Shell {
 		$this->out(__('Done.'));
 	}
 
+/**
+ * Get & configure the option parser
+ *
+ * @return void
+ */
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(__('CakePHP Language String Extraction:'))
+			->addOption('app', array('help' => __('Directory where your application is located.')))
+			->addOption('paths', array('help' => __('Comma separted list of paths, full paths are needed.')))
+			->addOption('merge', array(
+				'help' => __('Merge all domain strings into the default.po file.'),
+				'choices' => array('yes', 'no')
+			))
+			->addOption('output', array('help' => __('Full path to output directory.')))
+			->addOption('files', array('help' => __('Comma separated list of files, full paths are needed.')))
+			->addOption('exclude', array(
+				'help' => __('Comma separated list of directories to exclude. Any path containing a path segment with the provided values will be skipped. E.g. test,vendors')
+			));
+	}
+
 /**
  * Show help options
  *
@@ -484,9 +515,21 @@ class ExtractTask extends Shell {
  * @access private
  */
 	function __searchFiles() {
+		$pattern = false;
+		if (!empty($this->_exclude)) {
+			$pattern = '/[\/\\\\]' . implode('|', $this->_exclude) . '[\/\\\\]/'; 
+		}
 		foreach ($this->__paths as $path) {
 			$Folder = new Folder($path);
 			$files = $Folder->findRecursive('.*\.(php|ctp|thtml|inc|tpl)', true);
+			if (!empty($pattern)) {
+				foreach ($files as $i => $file) {
+					if (preg_match($pattern, $file)) {
+						unset($files[$i]);
+					}
+				}
+				$files = array_values($files);
+			}
 			$this->__files = array_merge($this->__files, $files);
 		}
 	}
diff --git a/cake/console/libs/tasks/fixture.php b/cake/console/shells/tasks/fixture.php
similarity index 89%
rename from cake/console/libs/tasks/fixture.php
rename to cake/console/shells/tasks/fixture.php
index c7d9cc8ba..d26144549 100644
--- a/cake/console/libs/tasks/fixture.php
+++ b/cake/console/shells/tasks/fixture.php
@@ -54,9 +54,38 @@ class FixtureTask extends BakeTask {
  * Override initialize
  *
  */
-	public function __construct(&$dispatch) {
-		parent::__construct($dispatch);
-		$this->path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS;
+	public function __construct($stdout = null, $stderr = null, $stdin = null) {
+		parent::__construct($stdout, $stderr, $stdin);
+		$this->path = APP . 'tests' . DS . 'fixtures' . DS;
+	}
+
+/**
+ * get the option parser.
+ *
+ * @return void
+ */
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+			__('Generate fixtures for use with the test suite. You can use `bake fixture all` to bake all fixtures.')
+		)->addArgument('name', array(
+			'help' => __('Name of the fixture to bake. Can use Plugin.name to bake plugin fixtures.')
+		))->addOption('count', array(
+			'help' => __('When using generated data, the number of records to include in the fixture(s).'),
+			'short' => 'n',
+			'default' => 10
+		))->addOption('connection', array(
+			'help' => __('Which database configuration to use for baking.'),
+			'short' => 'c',
+			'default' => 'default'
+		))->addOption('plugin', array(
+			'help' => __('CamelCased name of the plugin to bake fixtures for.'),
+			'short' => 'p',
+		))->addOption('records', array(
+			'help' => 'Used with --count and <name>/all commands to pull [n] records from the live tables, where [n] is either --count or the default of 10',
+			'short' => 'r',
+			'boolean' => true
+		))->epilog(__('Omitting all arguments and options will enter into an interactive mode.'));;
 	}
 
 /**
@@ -66,6 +95,7 @@ class FixtureTask extends BakeTask {
  * @return void
  */
 	public function execute() {
+		parent::execute();
 		if (empty($this->args)) {
 			$this->_interactive();
 		}
@@ -382,31 +412,4 @@ class FixtureTask extends BakeTask {
 		return $out;
 	}
 
-/**
- * Displays help contents
- *
- */
-	public function help() {
-		$this->hr();
-		$this->out("Usage: cake bake fixture <arg1> <params>");
-		$this->hr();
-		$this->out('Arguments:');
-		$this->out();
-		$this->out("<name>");
-		$this->out("\tName of the fixture to bake. Can use Plugin.name");
-		$this->out("\tas a shortcut for plugin baking.");
-		$this->out();
-		$this->out('Commands:');
-		$this->out("\nfixture <name>\n\tbakes fixture with specified name.");
-		$this->out("\nfixture all\n\tbakes all fixtures.");
-		$this->out();
-		$this->out('Parameters:');
-		$this->out("\t-count       When using generated data, the number of records to include in the fixture(s).");
-		$this->out("\t-connection  Which database configuration to use for baking.");
-		$this->out("\t-plugin      CamelCased name of plugin to bake fixtures for.");
-		$this->out("\t-records     Used with -count and <name>/all commands to pull [n] records from the live tables");
-		$this->out("\t             Where [n] is either -count or the default of 10.");
-		$this->out();
-		$this->_stop();
-	}
 }
diff --git a/cake/console/libs/tasks/model.php b/cake/console/shells/tasks/model.php
similarity index 97%
rename from cake/console/libs/tasks/model.php
rename to cake/console/shells/tasks/model.php
index 80f5c3717..6bc7c5c6a 100644
--- a/cake/console/libs/tasks/model.php
+++ b/cake/console/shells/tasks/model.php
@@ -74,6 +74,7 @@ class ModelTask extends BakeTask {
  */
 	public function execute() {
 		App::import('Model', 'Model', false);
+		parent::execute();
 
 		if (empty($this->args)) {
 			$this->_interactive();
@@ -873,31 +874,25 @@ class ModelTask extends BakeTask {
 	}
 
 /**
- * Displays help contents
+ * get the option parser.
  *
+ * @return void
  */
-	public function help() {
-		$this->hr();
-		$this->out("Usage: cake bake model <arg1>");
-		$this->hr();
-		$this->out('Arguments:');
-		$this->out();
-		$this->out("<name>");
-		$this->out("\tName of the model to bake. Can use Plugin.name");
-		$this->out("\tas a shortcut for plugin baking.");
-		$this->out();
-		$this->out('Commands:');
-		$this->out();
-		$this->out("model");
-		$this->out("\tbakes model in interactive mode.");
-		$this->out();
-		$this->out("model <name>");
-		$this->out("\tbakes model file with no associations or validation");
-		$this->out();
-		$this->out("model all");
-		$this->out("\tbakes all model files with associations and validation");
-		$this->out();
-		$this->_stop();
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+				__('Bake models.')
+			)->addArgument('name', array(
+				'help' => __('Name of the model to bake. Can use Plugin.name to bake plugin models.')
+			))->addSubcommand('all', array(
+				'help' => __('Bake all model files with associations and validation.')
+			))->addOption('plugin', array(
+				'short' => 'p',
+				'help' => __('Plugin to bake the model into.')
+			))->addOption('connection', array(
+				'short' => 'c',
+				'help' => __('The connection the model table is on.')
+			))->epilog(__('Omitting all arguments and options will enter into an interactive mode.'));
 	}
 
 /**
diff --git a/cake/console/libs/tasks/plugin.php b/cake/console/shells/tasks/plugin.php
similarity index 64%
rename from cake/console/libs/tasks/plugin.php
rename to cake/console/shells/tasks/plugin.php
index a59dec05d..4fca522dc 100644
--- a/cake/console/libs/tasks/plugin.php
+++ b/cake/console/shells/tasks/plugin.php
@@ -17,6 +17,7 @@
  * @since         CakePHP(tm) v 1.2
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
+App::import('Core', 'File');
 
 /**
  * Task class for creating a plugin
@@ -26,12 +27,6 @@
  */
 class PluginTask extends Shell {
 
-/**
- * Tasks
- *
- */
-	public $tasks = array('Model', 'Controller', 'View');
-
 /**
  * path to CONTROLLERS directory
  *
@@ -55,45 +50,20 @@ class PluginTask extends Shell {
  * @return void
  */
 	public function execute() {
-		if (empty($this->params['skel'])) {
-			$this->params['skel'] = '';
-			if (is_dir(CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel') === true) {
-				$this->params['skel'] = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel';
-			}
-		}
 		$plugin = null;
 
 		if (isset($this->args[0])) {
 			$plugin = Inflector::camelize($this->args[0]);
 			$pluginPath = $this->_pluginPath($plugin);
-			$this->Dispatch->shiftArgs();
 			if (is_dir($pluginPath)) {
 				$this->out(sprintf(__('Plugin: %s'), $plugin));
 				$this->out(sprintf(__('Path: %s'), $pluginPath));
-			} elseif (isset($this->args[0])) {
-				$this->err(sprintf(__('%s in path %s not found.'), $plugin, $pluginPath));
-				$this->_stop();
 			} else {
 				$this->_interactive($plugin);
 			}
 		} else {
 			return $this->_interactive();
 		}
-
-		if (isset($this->args[0])) {
-			$task = Inflector::classify($this->args[0]);
-			$this->Dispatch->shiftArgs();
-			if (in_array($task, $this->tasks)) {
-				$this->{$task}->plugin = $plugin;
-				$this->{$task}->path = $pluginPath . Inflector::underscore(Inflector::pluralize($task)) . DS;
-
-				if (!is_dir($this->{$task}->path)) {
-					$this->err(sprintf(__("%s directory could not be found.\nBe sure you have created %s"), $task, $this->{$task}->path));
-				}
-				$this->{$task}->loadTasks();
-				return $this->{$task}->execute();
-			}
-		}
 	}
 
 /**
@@ -108,7 +78,7 @@ class PluginTask extends Shell {
 		}
 
 		if (!$this->bake($plugin)) {
-			$this->err(sprintf(__("An error occured trying to bake: %s in %s"), $plugin, $this->path . Inflector::underscore($pluginPath)));
+			$this->error(sprintf(__("An error occured trying to bake: %s in %s"), $plugin, $this->path . Inflector::underscore($pluginPath)));
 		}
 	}
 
@@ -127,20 +97,19 @@ class PluginTask extends Shell {
 			$this->findPath($pathOptions);
 		}
 		$this->hr();
-		$this->out(sprintf(__("Plugin Name: %s"),  $plugin));
-		$this->out(sprintf(__("Plugin Directory: %s"), $this->path . $pluginPath));
+		$this->out(sprintf(__("<info>Plugin Name:</info> %s"),  $plugin));
+		$this->out(sprintf(__("<info>Plugin Directory:</info> %s"), $this->path . $pluginPath));
 		$this->hr();
 
 		$looksGood = $this->in(__('Look okay?'), array('y', 'n', 'q'), 'y');
 
 		if (strtolower($looksGood) == 'y') {
-			$verbose = $this->in(__('Do you want verbose output?'), array('y', 'n'), 'n');
-
 			$Folder =& new Folder($this->path . $pluginPath);
 			$directories = array(
 				'config' . DS . 'schema',
 				'models' . DS . 'behaviors',
 				'models' . DS . 'datasources',
+				'console' . DS . 'shells' . DS . 'tasks',
 				'controllers' . DS . 'components',
 				'libs',
 				'views' . DS . 'helpers',
@@ -152,7 +121,6 @@ class PluginTask extends Shell {
 				'tests' . DS . 'groups',
 				'tests' . DS . 'fixtures',
 				'vendors',
-				'vendors' . DS . 'shells' . DS . 'tasks',
 				'webroot'
 			);
 
@@ -162,10 +130,8 @@ class PluginTask extends Shell {
 				$File =& new File($dirPath . DS . 'empty', true);
 			}
 
-			if (strtolower($verbose) == 'y') {
-				foreach ($Folder->messages() as $message) {
-					$this->out($message);
-				}
+			foreach ($Folder->messages() as $message) {
+				$this->out($message, 1, Shell::VERBOSE);
 			}
 
 			$errors = $Folder->errors();
@@ -190,8 +156,7 @@ class PluginTask extends Shell {
 			$this->createFile($this->path . $pluginPath . DS . $modelFileName, $out);
 
 			$this->hr();
-			$this->out(sprintf(__('Created: %s in %s'), $plugin, $this->path . $pluginPath));
-			$this->hr();
+			$this->out(sprintf(__('<success>Created:</success> %s in %s'), $plugin, $this->path . $pluginPath), 2);
 		}
 
 		return true;
@@ -219,28 +184,19 @@ class PluginTask extends Shell {
 	}
 
 /**
- * Help
+ * get the option parser for the plugin task
  *
  * @return void
  */
-	public function help() {
-		$this->hr();
-		$this->out("Usage: cake bake plugin <arg1> <arg2>...");
-		$this->hr();
-		$this->out('Commands:');
-		$this->out();
-		$this->out("plugin <name>");
-		$this->out("\tbakes plugin directory structure");
-		$this->out();
-		$this->out("plugin <name> model");
-		$this->out("\tbakes model. Run 'cake bake model help' for more info.");
-		$this->out();
-		$this->out("plugin <name> controller");
-		$this->out("\tbakes controller. Run 'cake bake controller help' for more info.");
-		$this->out();
-		$this->out("plugin <name> view");
-		$this->out("\tbakes view. Run 'cake bake view help' for more info.");
-		$this->out();
-		$this->_stop();
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+			'Create the directory structure, AppModel and AppController classes for a new plugin. ' .
+			'Can create plugins in any of your bootstrapped plugin paths.'
+		)->addArgument('name', array(
+			'help' => __('CamelCased name of the plugin to create.')
+		));
+
 	}
+
 }
diff --git a/cake/console/libs/tasks/project.php b/cake/console/shells/tasks/project.php
similarity index 61%
rename from cake/console/libs/tasks/project.php
rename to cake/console/shells/tasks/project.php
index 3cc1270bc..3710a9906 100644
--- a/cake/console/libs/tasks/project.php
+++ b/cake/console/shells/tasks/project.php
@@ -41,35 +41,34 @@ class ProjectTask extends Shell {
  *
  * @param string $project Project path
  */
-	public function execute($project = null) {
-		if ($project === null) {
-			if (isset($this->args[0])) {
-				$project = $this->args[0];
-			}
+	public function execute() {
+		$project = null;
+		if (isset($this->args[0])) {
+			$project = $this->args[0];
 		}
 
-		if ($project) {
-			$this->Dispatch->parseParams(array('-app', $project));
-			$project = $this->params['working'];
+		if ($project && isset($_SERVER['PWD'])) {
+			$project = $_SERVER['PWD'] . DS . $project;
 		}
 
 		if (empty($this->params['skel'])) {
-			$this->params['skel'] = '';
-			if (is_dir(CAKE . 'console' . DS . 'templates' . DS . 'skel') === true) {
-				$this->params['skel'] = CAKE . 'console' . DS . 'templates' . DS . 'skel';
+			$core = App::core('shells');
+			$skelPath = dirname($core[0]) . DS . 'templates' . DS . 'skel';
+			if (is_dir($skelPath) === true) {
+				$this->params['skel'] = $skelPath;
 			}
 		}
 
 		while (!$project) {
 			$prompt = __("What is the full path for this app including the app directory name?\n Example:");
-			$default = $this->params['working'] . DS . 'myapp';
+			$default = APP_PATH . 'myapp';
 			$project = $this->in($prompt . $default, null, $default);
 		}
 
 		if ($project) {
 			$response = false;
 			while ($response == false && is_dir($project) === true && file_exists($project . 'config' . 'core.php')) {
-				$prompt = sprintf(__('A project already exists in this location: %s Overwrite?'), $project);
+				$prompt = sprintf(__('<warning>A project already exists in this location:</warning> %s Overwrite?'), $project);
 				$response = $this->in($prompt, array('y','n'), 'n');
 				if (strtolower($response) === 'n') {
 					$response = $project = false;
@@ -77,43 +76,57 @@ class ProjectTask extends Shell {
 			}
 		}
 
+		$success = true;
 		if ($this->bake($project)) {
 			$path = Folder::slashTerm($project);
 			if ($this->createHome($path)) {
-				$this->out(__('Welcome page created'));
+				$this->out(__(' * Welcome page created'));
 			} else {
-				$this->out(__('The Welcome page was NOT created'));
+				$this->err(__('The Welcome page was <error>NOT</error> created'));
+				$success = false;
 			}
 
-			if ($this->securitySalt($path) === true ) {
-				$this->out(__('Random hash key created for \'Security.salt\''));
+			if ($this->securitySalt($path) === true) {
+				$this->out(__(' * Random hash key created for \'Security.salt\''));
 			} else {
 				$this->err(sprintf(__('Unable to generate random hash for \'Security.salt\', you should change it in %s'), CONFIGS . 'core.php'));
+				$success = false;
 			}
 
-			if ($this->securityCipherSeed($path) === true ) {
-				$this->out(__('Random seed created for \'Security.cipherSeed\''));
+			if ($this->securityCipherSeed($path) === true) {
+				$this->out(__(' * Random seed created for \'Security.cipherSeed\''));
 			} else {
 				$this->err(sprintf(__('Unable to generate random seed for \'Security.cipherSeed\', you should change it in %s'), CONFIGS . 'core.php'));
+				$success = false;
 			}
 
-			$corePath = $this->corePath($path);
-			if ($corePath === true ) {
-				$this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php'), CAKE_CORE_INCLUDE_PATH));
-				$this->out(sprintf(__('CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php'), CAKE_CORE_INCLUDE_PATH));
-				$this->out(__('Remember to check these value after moving to production server'));
-			} elseif ($corePath === false) {
+			if ($this->corePath($path) === true) {
+				$this->out(sprintf(__(' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/index.php'), CAKE_CORE_INCLUDE_PATH));
+				$this->out(sprintf(__(' * CAKE_CORE_INCLUDE_PATH set to %s in webroot/test.php'), CAKE_CORE_INCLUDE_PATH));
+				$this->out(__('   * <warning>Remember to check these value after moving to production server</warning>'));
+			} else {
 				$this->err(sprintf(__('Unable to set CAKE_CORE_INCLUDE_PATH, you should change it in %s'), $path . 'webroot' .DS .'index.php'));
+				$success = false;
 			}
+			if ($this->consolePath($path) === true) {
+				$this->out(__(' * app/console/cake.php path set.'));
+			} else {
+				$this->err(__('Unable to set console path for app/console.'));
+				$success = false;
+			}
+
 			$Folder = new Folder($path);
 			if (!$Folder->chmod($path . 'tmp', 0777)) {
 				$this->err(sprintf(__('Could not set permissions on %s'), $path . DS .'tmp'));
 				$this->out(sprintf(__('chmod -R 0777 %s'), $path . DS .'tmp'));
+				$success = false;
 			}
-
-			$this->params['working'] = $path;
-			$this->params['app'] = basename($path);
-			return true;
+			if ($success) {
+				$this->out(__('<success>Project baked successfully!</success>'));
+			} else {
+				$this->out(__('Project baked but with <warning>some issues.</warning>.'));
+			}
+			return $path;
 		}
 	}
 
@@ -135,7 +148,7 @@ class ProjectTask extends Shell {
 		while (!$skel) {
 			$skel = $this->in(sprintf(__("What is the path to the directory layout you wish to copy?\nExample: %s"), APP, null, ROOT . DS . 'myapp' . DS));
 			if ($skel == '') {
-				$this->out(__('The directory path you supplied was empty. Please try again.'));
+				$this->err(__('The directory path you supplied was empty. Please try again.'));
 			} else {
 				while (is_dir($skel) === false) {
 					$skel = $this->in(__('Directory path does not exist please choose another:'));
@@ -145,33 +158,29 @@ class ProjectTask extends Shell {
 
 		$app = basename($path);
 
-		$this->out(__('Bake Project'));
-		$this->out(__('Skel Directory: ') . $skel);
-		$this->out(__('Will be copied to: ') . $path);
+		$this->out(__('<info>Skel Directory</info>: ') . $skel);
+		$this->out(__('<info>Will be copied to</info>: ') . $path);
 		$this->hr();
 
 		$looksGood = $this->in(__('Look okay?'), array('y', 'n', 'q'), 'y');
 
 		if (strtolower($looksGood) == 'y') {
-			$verbose = $this->in(__('Do you want verbose output?'), array('y', 'n'), 'n');
-
 			$Folder = new Folder($skel);
 			if (!empty($this->params['empty'])) {
 				$skip = array();
 			}
+
 			if ($Folder->copy(array('to' => $path, 'skip' => $skip))) {
 				$this->hr();
-				$this->out(sprintf(__('Created: %s in %s'), $app, $path));
+				$this->out(sprintf(__('<success>Created:</success> %s in %s'), $app, $path));
 				$this->hr();
 			} else {
-				$this->err(sprintf(__(" '%s' could not be created properly"), $app));
+				$this->err(sprintf(__("<error>Could not create</error> '%s' properly."), $app));
 				return false;
 			}
 
-			if (strtolower($verbose) == 'y') {
-				foreach ($Folder->messages() as $message) {
-					$this->out($message);
-				}
+			foreach ($Folder->messages() as $message) {
+				$this->out(String::wrap(' * ' . $message), 1, Shell::VERBOSE);
 			}
 
 			return true;
@@ -197,6 +206,28 @@ class ProjectTask extends Shell {
 		return $this->createFile($path.'home.ctp', $output);
 	}
 
+/**
+ * Generates the correct path to the CakePHP libs that are generating the project
+ * and points app/console/cake.php to the right place
+ *
+ * @param string $path Project path.
+ * @return boolean success
+ */
+	public function consolePath($path) {
+		$File = new File($path . 'console' . DS . 'cake.php');
+		$contents = $File->read();
+		if (preg_match('/(__CAKE_PATH__)/', $contents, $match)) {
+			$path = CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'console' . DS;
+			$replacement = "'" . str_replace(DS, "' . DIRECTORY_SEPARATOR . '", $path) . "'";
+			$result = str_replace($match[0], $replacement, $contents);
+			if ($File->write($result)) {
+				return true;
+			}
+			return false;
+		}
+		return false;
+	}
+
 /**
  * Generates and writes 'Security.salt'
  *
@@ -204,9 +235,9 @@ class ProjectTask extends Shell {
  * @return boolean Success
  */
 	public function securitySalt($path) {
-		$File =& new File($path . 'config' . DS . 'core.php');
+		$File = new File($path . 'config' . DS . 'core.php');
 		$contents = $File->read();
-		if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.salt\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
+		if (preg_match('/([\s]*Configure::write\(\'Security.salt\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
 			if (!class_exists('Security')) {
 				require LIBS . 'security.php';
 			}
@@ -220,28 +251,28 @@ class ProjectTask extends Shell {
 		return false;
 	}
 
-	/**
-	 * Generates and writes 'Security.cipherSeed'
-	 *
-	 * @param string $path Project path
-	 * @return boolean Success
-		 */
-		public function securityCipherSeed($path) {
-			$File =& new File($path . 'config' . DS . 'core.php');
-			$contents = $File->read();
-			if (preg_match('/([\\t\\x20]*Configure::write\\(\\\'Security.cipherSeed\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
-				if (!class_exists('Security')) {
-					require LIBS . 'security.php';
-				}
-				$string = substr(bin2hex(Security::generateAuthKey()), 0, 30);
-				$result = str_replace($match[0], "\t" . 'Configure::write(\'Security.cipherSeed\', \''.$string.'\');', $contents);
-				if ($File->write($result)) {
-					return true;
-				}
-				return false;
+/**
+ * Generates and writes 'Security.cipherSeed'
+ *
+ * @param string $path Project path
+ * @return boolean Success
+	 */
+	public function securityCipherSeed($path) {
+		$File = new File($path . 'config' . DS . 'core.php');
+		$contents = $File->read();
+		if (preg_match('/([\s]*Configure::write\(\'Security.cipherSeed\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
+			if (!class_exists('Security')) {
+				require LIBS . 'security.php';
+			}
+			$string = substr(bin2hex(Security::generateAuthKey()), 0, 30);
+			$result = str_replace($match[0], "\t" . 'Configure::write(\'Security.cipherSeed\', \''.$string.'\');', $contents);
+			if ($File->write($result)) {
+				return true;
 			}
 			return false;
 		}
+		return false;
+	}
 
 /**
  * Generates and writes CAKE_CORE_INCLUDE_PATH
@@ -251,9 +282,9 @@ class ProjectTask extends Shell {
  */
 	public function corePath($path) {
 		if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) {
-			$File =& new File($path . 'webroot' . DS . 'index.php');
+			$File = new File($path . 'webroot' . DS . 'index.php');
 			$contents = $File->read();
-			if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
+			if (preg_match('/([\s]*define\(\'CAKE_CORE_INCLUDE_PATH\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
 				$root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'";
 				$result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $contents);
 				if (!$File->write($result)) {
@@ -263,9 +294,9 @@ class ProjectTask extends Shell {
 				return false;
 			}
 
-			$File =& new File($path . 'webroot' . DS . 'test.php');
+			$File = new File($path . 'webroot' . DS . 'test.php');
 			$contents = $File->read();
-			if (preg_match('/([\\t\\x20]*define\\(\\\'CAKE_CORE_INCLUDE_PATH\\\',[\\t\\x20\'A-z0-9]*\\);)/', $contents, $match)) {
+			if (preg_match('/([\s]*define\(\'CAKE_CORE_INCLUDE_PATH\',[\s\'A-z0-9]*\);)/', $contents, $match)) {
 				$result = str_replace($match[0], "\t\tdefine('CAKE_CORE_INCLUDE_PATH', " . $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "');", $contents);
 				if (!$File->write($result)) {
 					return false;
@@ -285,9 +316,9 @@ class ProjectTask extends Shell {
  */
 	public function cakeAdmin($name) {
 		$path = (empty($this->configPath)) ? CONFIGS : $this->configPath;
-		$File =& new File($path . 'core.php');
+		$File = new File($path . 'core.php');
 		$contents = $File->read();
-		if (preg_match('%([/\\t\\x20]*Configure::write\(\'Routing.prefixes\',[\\t\\x20\'a-z,\)\(]*\\);)%', $contents, $match)) {
+		if (preg_match('%([/\s]*Configure::write\(\'Routing.prefixes\',[\s\'a-z,\)\(]*\);)%', $contents, $match)) {
 			$result = str_replace($match[0], "\t" . 'Configure::write(\'Routing.prefixes\', array(\''.$name.'\'));', $contents);
 			if ($File->write($result)) {
 				Configure::write('Routing.prefixes', array($name));
@@ -335,7 +366,7 @@ class ProjectTask extends Shell {
 				$admin = $this->in(__('Enter a routing prefix:'), null, 'admin');
 			}
 			if ($this->cakeAdmin($admin) !== true) {
-				$this->out(__('Unable to write to /app/config/core.php.'));
+				$this->out(__('<error>Unable to write to</error> /app/config/core.php.'));
 				$this->out('You need to enable Configure::write(\'Routing.prefixes\',array(\'admin\')) in /app/config/core.php to use prefix routing.');
 				$this->_stop();
 			}
@@ -345,21 +376,21 @@ class ProjectTask extends Shell {
 	}
 
 /**
- * Help
+ * get the option parser.
  *
- * @return void
+ * @return ConsoleOptionParser
  */
-	public function help() {
-		$this->hr();
-		$this->out("Usage: cake bake project <arg1>");
-		$this->hr();
-		$this->out('Commands:');
-		$this->out();
-		$this->out("project <name>");
-		$this->out("\tbakes app directory structure.");
-		$this->out("\tif <name> begins with '/' path is absolute.");
-		$this->out();
-		$this->_stop();
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+				__('Generate a new CakePHP project skeleton.')
+			)->addArgument('name', array(
+				'help' => __('Application directory to make, if it starts with "/" the path is absolute.')
+			))->addOption('empty', array(
+				'help' => __('Create empty files in each of the directories. Good if you are using git')
+			))->addOption('skel', array(
+				'help' => __('The directory layout to use for the new application skeleton. Defaults to cake/console/templates/skel of CakePHP used to create the project.')
+			));
 	}
 
 }
diff --git a/cake/console/libs/tasks/template.php b/cake/console/shells/tasks/template.php
similarity index 96%
rename from cake/console/libs/tasks/template.php
rename to cake/console/shells/tasks/template.php
index 4d47b5a10..b9e267087 100644
--- a/cake/console/libs/tasks/template.php
+++ b/cake/console/shells/tasks/template.php
@@ -17,6 +17,8 @@
  * @since         CakePHP(tm) v 1.3
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
+App::import('Core', 'Folder');
+
 class TemplateTask extends Shell {
 
 /**
@@ -55,7 +57,7 @@ class TemplateTask extends Shell {
 		$paths = App::path('shells');
 		$core = array_pop($paths);
 		$separator = DS === '/' ? '/' : '\\\\';
-		$core = preg_replace('#libs' . $separator . '$#', '', $core);
+		$core = preg_replace('#shells' . $separator . '$#', '', $core);
 		$paths[] = $core;
 		$Folder =& new Folder($core . 'templates' . DS . 'default');
 		$contents = $Folder->read();
@@ -63,6 +65,7 @@ class TemplateTask extends Shell {
 
 		$plugins = App::objects('plugin');
 		foreach ($plugins as $plugin) {
+			$paths[] = $this->_pluginPath($plugin) . 'console' . DS . 'shells' . DS;
 			$paths[] = $this->_pluginPath($plugin) . 'vendors' . DS . 'shells' . DS;
 		}
 
@@ -178,7 +181,7 @@ class TemplateTask extends Shell {
 		}
 		$index = $this->in(__('Which bake theme would you like to use?'), range(1, $i - 1), 1);
 		$themeNames = array_keys($this->templatePaths);
-		$this->Dispatch->params['theme'] = $themeNames[$index - 1];
+		$this->params['theme'] = $themeNames[$index - 1];
 		return $indexedPaths[$index];
 	}
 
diff --git a/cake/console/libs/tasks/test.php b/cake/console/shells/tasks/test.php
similarity index 94%
rename from cake/console/libs/tasks/test.php
rename to cake/console/shells/tasks/test.php
index f64e9bdc1..ce300f058 100644
--- a/cake/console/libs/tasks/test.php
+++ b/cake/console/shells/tasks/test.php
@@ -60,12 +60,12 @@ class TestTask extends BakeTask {
  */
 	protected $_fixtures = array();
 
-
 /**
  * Execution method always used for tasks
  *
  */
 	public function execute() {
+		parent::execute();
 		if (empty($this->args)) {
 			$this->_interactive();
 		}
@@ -77,7 +77,7 @@ class TestTask extends BakeTask {
 		if (count($this->args) > 1) {
 			$type = Inflector::underscore($this->args[0]);
 			if ($this->bake($type, $this->args[1])) {
-				$this->out('done');
+				$this->out('<success>Done</success>');
 			}
 		}
 	}
@@ -412,27 +412,21 @@ class TestTask extends BakeTask {
 	}
 
 /**
- * Show help file.
+ * get the option parser.
  *
  * @return void
  */
-	public function help() {
-		$this->hr();
-		$this->out("Usage: cake bake test <type> <class>");
-		$this->hr();
-		$this->out('Commands:');
-		$this->out("");
-		$this->out("test model post\n\tbakes a test case for the post model.");
-		$this->out("");
-		$this->out("test controller comments\n\tbakes a test case for the comments controller.");
-		$this->out("");
-		$this->out('Arguments:');
-		$this->out("\t<type>   Can be any of the following 'controller', 'model', 'helper',\n\t'component', 'behavior'.");
-		$this->out("\t<class>  Any existing class for the chosen type.");
-		$this->out("");
-		$this->out("Parameters:");
-		$this->out("\t-plugin  CamelCased name of plugin to bake tests for.");
-		$this->out("");
-		$this->_stop();
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(__('Bake test case skeletons for classes.'))
+			->addArgument('type', array(
+				'help' => __('Type of class to bake, can be any of the following: controller, model, helper, component or behavior.'),
+				'choices' => array('controller', 'model', 'helper', 'component', 'behavior')
+			))->addArgument('name', array(
+				'help' => __('An existing class to bake tests for.')
+			))->addOption('plugin', array(
+				'short' => 'p',
+				'help' => __('CamelCased name of the plugin to bake tests for.')
+			))->epilog(__('Omitting all arguments and options will enter into an interactive mode.'));
 	}
 }
diff --git a/cake/console/libs/tasks/view.php b/cake/console/shells/tasks/view.php
similarity index 90%
rename from cake/console/libs/tasks/view.php
rename to cake/console/shells/tasks/view.php
index f4f4e95bd..c166a003d 100644
--- a/cake/console/libs/tasks/view.php
+++ b/cake/console/shells/tasks/view.php
@@ -97,6 +97,7 @@ class ViewTask extends BakeTask {
  *
  */
 	public function execute() {
+		parent::execute();
 		if (empty($this->args)) {
 			$this->_interactive();
 		}
@@ -421,43 +422,32 @@ class ViewTask extends BakeTask {
 	}
 
 /**
- * Displays help contents
+ * get the option parser for this task
  *
+ * @return ConsoleOptionParser
  */
-	public function help() {
-		$this->hr();
-		$this->out("Usage: cake bake view <arg1> <arg2>...");
-		$this->hr();
-		$this->out('Arguments:');
-		$this->out();
-		$this->out("<controller>");
-		$this->out("\tName of the controller views to bake. Can use Plugin.name");
-		$this->out("\tas a shortcut for plugin baking.");
-		$this->out();
-		$this->out("<action>");
-		$this->out("\tName of the action view to bake");
-		$this->out();
-		$this->out('Commands:');
-		$this->out();
-		$this->out("view <controller>");
-		$this->out("\tWill read the given controller for methods");
-		$this->out("\tand bake corresponding views.");
-		$this->out("\tUsing the -admin flag will only bake views for actions");
-		$this->out("\tthat begin with Routing.prefixes.");
-		$this->out("\tIf var scaffold is found it will bake the CRUD actions");
-		$this->out("\t(index,view,add,edit)");
-		$this->out();
-		$this->out("view <controller> <action>");
-		$this->out("\tWill bake a template. core templates: (index, add, edit, view)");
-		$this->out();
-		$this->out("view <controller> <template> <alias>");
-		$this->out("\tWill use the template specified");
-		$this->out("\tbut name the file based on the alias");
-		$this->out();
-		$this->out("view all");
-		$this->out("\tBake all CRUD action views for all controllers.");
-		$this->out("\tRequires that models and controllers exist.");
-		$this->_stop();
+	public function getOptionParser() {
+		$parser = parent::getOptionParser();
+		return $parser->description(
+			__('Bake views for a controller, using built-in or custom templates.')
+		)->addArgument('controller', array(
+			'help' => __('Name of the controller views to bake.  Can be Plugin.name as a shortcut for plugin baking.')
+		))->addArgument('action', array(
+			'help' => __("Will bake a single action's file. core templates are (index, add, edit, view)")
+		))->addArgument('alias', array(
+			'help' => __('Will bake the template in <action> but create the filename after <alias>.')
+		))->addOption('plugin', array(
+			'short' => 'p',
+			'help' => __('Plugin to bake the view into.')
+		))->addOption('admin', array(
+			'help' => __('Set to only bake views for a prefix in Routing.prefixes'),
+			'boolean' => true
+		))->addOption('connection', array(
+			'short' => 'c',
+			'help' => __('The connection the connected model is on.')
+		))->addSubcommand('all', array(
+			'help' => __('Bake all CRUD action views for all controllers. Requires models and controllers to exist.')
+		))->epilog(__('Omitting all arguments and options will enter into an interactive mode.'));
 	}
 
 /**
diff --git a/cake/console/libs/testsuite.php b/cake/console/shells/testsuite.php
similarity index 90%
rename from cake/console/libs/testsuite.php
rename to cake/console/shells/testsuite.php
index 265cf5486..7d9356762 100644
--- a/cake/console/libs/testsuite.php
+++ b/cake/console/shells/testsuite.php
@@ -28,6 +28,29 @@ class TestSuiteShell extends Shell {
  */
 	protected $_dispatcher = null;
 
+/**
+ * get the option parser for the test suite.
+ *
+ * @return void
+ */
+	public function getOptionParser() {
+		$parser = new ConsoleOptionParser($this->name);
+		$parser->description(array(
+			'The CakePHP Testsuite allows you to run test cases from the command line',
+			'If run with no command line arguments, a list of available core test cases will be shown'
+		))->addArgument('category', array(
+			'help' => __('app, core or name of a plugin.'),
+			'required' => true
+		))->addArgument('file', array(
+			'help' => __('file name with folder prefix and without the test.php suffix.'),
+			'required' => true,
+		))->addOption('filter', array(
+			'help' => __('Filter which tests to run.'),
+			'default' => false
+		));
+		return $parser;
+	}
+
 /**
  * Initialization method installs Simpletest and loads all plugins
  *
@@ -74,9 +97,6 @@ class TestSuiteShell extends Shell {
 		if (isset($this->args[1])) {
 			$params['case'] = Inflector::underscore($this->args[1]);
 		}
-		if (isset($this->params['filter'])) {
-			$this->params['-filter'] = $this->params['filter'];
-		}
 		return $params;
 	}
 
@@ -87,12 +107,13 @@ class TestSuiteShell extends Shell {
  */
 	protected function runnerOptions() {
 		$options = array();
-		foreach ($this->params as $param => $value) {
-			if ($param[0] === '-') {
-				$options[] = '-' . $param;
-				if (is_string($value)) {
-					$options[] = $value;
-				}
+		$params = $this->params;
+		unset($params['help']);
+		$params = array_filter($params);
+		foreach ($params as $param => $value) {
+			$options[] = '--' . $param;
+			if (is_string($value)) {
+				$options[] = $value;
 			}
 		}
 		return $options;
diff --git a/cake/console/templates/skel/console/cake b/cake/console/templates/skel/console/cake
new file mode 100644
index 000000000..d4d067d59
--- /dev/null
+++ b/cake/console/templates/skel/console/cake
@@ -0,0 +1,26 @@
+#!/bin/bash
+################################################################################
+#
+# Bake is a shell script for running CakePHP bake script
+# PHP 5
+#
+# CakePHP(tm) :  Rapid Development Framework (http://cakephp.org)
+# Copyright 2005-2010, Cake Software Foundation, Inc.
+#
+# Licensed under The MIT License
+# Redistributions of files must retain the above copyright notice.
+#
+# @copyright		Copyright 2005-2010, Cake Software Foundation, Inc.
+# @link				http://cakephp.org CakePHP(tm) Project
+# @package			cake
+# @subpackage		cake.app.console
+# @since			CakePHP(tm) v 2.0
+# @license			MIT License (http://www.opensource.org/licenses/mit-license.php)
+#
+################################################################################
+LIB=${0/%cake/}
+APP=`pwd`
+
+exec php -q ${LIB}cake.php -working "${APP}" "$@"
+
+exit;
\ No newline at end of file
diff --git a/cake/console/templates/skel/console/cake.bat b/cake/console/templates/skel/console/cake.bat
new file mode 100644
index 000000000..84ed04a4f
--- /dev/null
+++ b/cake/console/templates/skel/console/cake.bat
@@ -0,0 +1,33 @@
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+::
+:: Bake is a shell script for running CakePHP bake script
+:: PHP 5
+::
+:: CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+:: Copyright 2005-2010, Cake Software Foundation, Inc.
+::
+:: Licensed under The MIT License
+:: Redistributions of files must retain the above copyright notice.
+::
+:: @copyright		Copyright 2005-2010, Cake Software Foundation, Inc.
+:: @link			http://cakephp.org CakePHP(tm) Project
+:: @package			cake
+:: @subpackage		cake.app.console
+:: @since			CakePHP(tm) v 2.0
+:: @license			MIT License (http://www.opensource.org/licenses/mit-license.php)
+::
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+:: In order for this script to work as intended, the cake\console\ folder must be in your PATH
+
+@echo.
+@echo off
+
+SET app=%0
+SET lib=%~dp0
+
+php -q "%lib%cake.php" -working "%CD%" %*
+
+echo.
+
+exit /B %ERRORLEVEL%
\ No newline at end of file
diff --git a/cake/console/templates/skel/console/cake.php b/cake/console/templates/skel/console/cake.php
new file mode 100644
index 000000000..8ccac0bfb
--- /dev/null
+++ b/cake/console/templates/skel/console/cake.php
@@ -0,0 +1,25 @@
+#!/usr/bin/php -q
+<?php
+/**
+ * Command-line code generation utility to automate programmer chores.
+ *
+ * Shell dispatcher class
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.cake.console
+ * @since         CakePHP(tm) v 1.2.0.5012
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+require_once(__CAKE_PATH__ . 'shell_dispatcher.php');
+
+return ShellDispatcher::run($argv);
diff --git a/cake/tests/test_app/plugins/test_plugin/vendors/shells/templates/empty b/cake/console/templates/skel/console/shells/tasks/empty
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin/vendors/shells/templates/empty
rename to cake/console/templates/skel/console/shells/tasks/empty
diff --git a/cake/console/templates/skel/tmp/cache/models/empty b/cake/console/templates/skel/tmp/cache/models/empty
old mode 100644
new mode 100755
diff --git a/cake/console/templates/skel/tmp/cache/persistent/empty b/cake/console/templates/skel/tmp/cache/persistent/empty
old mode 100644
new mode 100755
diff --git a/cake/console/templates/skel/tmp/cache/views/empty b/cake/console/templates/skel/tmp/cache/views/empty
old mode 100644
new mode 100755
diff --git a/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/tasks/empty b/cake/console/templates/skel/vendors/empty
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin_two/vendors/shells/tasks/empty
rename to cake/console/templates/skel/vendors/empty
diff --git a/cake/libs/configure.php b/cake/libs/configure.php
index bee30774d..9373f907f 100644
--- a/cake/libs/configure.php
+++ b/cake/libs/configure.php
@@ -587,7 +587,11 @@ class App {
 			'views' => array(VIEWS),
 			'helpers' => array(HELPERS),
 			'locales' => array(APP . 'locale' . DS),
-			'shells' => array(APP . 'vendors' . DS . 'shells' . DS, VENDORS . 'shells' . DS),
+			'shells' => array(
+				APP . 'console' . DS . 'shells' . DS,
+				APP . 'vendors' . DS . 'shells' . DS, 
+				VENDORS . 'shells' . DS
+			),
 			'vendors' => array(APP . 'vendors' . DS, VENDORS),
 			'plugins' => array(APP . 'plugins' . DS)
 		);
@@ -690,7 +694,9 @@ class App {
 			$paths['helpers'][] = $libs . 'view' . DS . 'helpers' . DS;
 			$paths['plugins'][] = $path . 'plugins' . DS;
 			$paths['vendors'][] = $path . 'vendors' . DS;
-			$paths['shells'][] = $cake . 'console' . DS . 'libs' . DS;
+			$paths['shells'][] = $cake . 'console' . DS . 'shells' . DS;
+			// Provide BC path to vendors/shells
+			$paths['shells'][] = $path . 'vendors' . DS . 'shells' . DS;
 
 			Cache::write('core_paths', array_filter($paths), '_cake_core_');
 		}
@@ -1136,6 +1142,15 @@ class App {
 				}
 				return array('class' => $type, 'suffix' => null, 'path' => $path);
 			break;
+			case 'shell':
+				if (!class_exists('Shell')) {
+					App::import($type, 'Shell', false);
+				}
+				if ($plugin) {
+					$path = $pluginPath . DS . 'console' . DS . 'shells' . DS;
+				}
+				return array('class' => $type, 'suffix' => null, 'path' => $path);
+			break;
 			case 'vendor':
 				if ($plugin) {
 					$path = $pluginPath . DS . 'vendors' . DS;
diff --git a/cake/libs/exceptions.php b/cake/libs/exceptions.php
index fc153a0fe..99cc3a946 100644
--- a/cake/libs/exceptions.php
+++ b/cake/libs/exceptions.php
@@ -333,6 +333,33 @@ class MissingTaskClassException extends CakeException {
 	protected $_messageTemplate = 'Task class "%s" is missing.';
 }
 
+/**
+ * Used when a shell method cannot be found.
+ *
+ * @package cake.libs
+ */
+class MissingShellMethodException extends CakeException { 
+	protected $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s --help`";
+}
+
+/**
+ * Used when a shell class cannot be found.
+ *
+ * @package cake.libs
+ */
+class MissingShellClassException extends CakeException { 
+	protected $_messageTemplate = "Shell class %s could not be loaded.";
+}
+
+/**
+ * Used when a shell class cannot be found.
+ *
+ * @package cake.libs
+ */
+class MissingShellFileException extends CakeException { 
+	protected $_messageTemplate = "Shell file %s could not be loaded.";
+}
+
 /**
  * Exception class to be thrown when a database table is not found in the datasource
  *
diff --git a/cake/libs/string.php b/cake/libs/string.php
index 15048af63..affcc263b 100644
--- a/cake/libs/string.php
+++ b/cake/libs/string.php
@@ -319,4 +319,38 @@ class String {
 		}
 		return $str;
 	}
+
+/**
+ * Wraps text to a specific width, can optionally wrap at word breaks.
+ *
+ * ### Options
+ *
+ * - `width` The width to wrap to.  Defaults to 72
+ * - `wordWrap` Only wrap on words breaks (spaces) Defaults to true.
+ * - `indent` String to indent with. Defaults to null.
+ * - `indentAt` 0 based index to start indenting at. Defaults to 0.
+ *
+ * @param string $text Text the text to format.
+ * @param mixed $options Array of options to use, or an integer to wrap the text to.
+ * @return string Formatted text.
+ */
+	public static function wrap($text, $options = array()) {
+		if (is_numeric($options)) {
+			$options = array('width' => $options);
+		}
+		$options += array('width' => 72, 'wordWrap' => true, 'indent' => null, 'indentAt' => 0);
+		if ($options['wordWrap']) {
+			$wrapped = wordwrap($text, $options['width'], "\n");
+		} else {
+			$wrapped = trim(chunk_split($text, $options['width'] - 1, "\n"));
+		}
+		if (!empty($options['indent'])) {
+			$chunks = explode("\n", $wrapped);
+			for ($i = $options['indentAt'], $len = count($chunks); $i < $len; $i++) {
+				$chunks[$i] = $options['indent'] . $chunks[$i];
+			}
+			$wrapped = implode("\n", $chunks);
+		}
+		return $wrapped;
+	}
 }
diff --git a/cake/tests/cases/basics.test.php b/cake/tests/cases/basics.test.php
index 723254a20..6972e0315 100644
--- a/cake/tests/cases/basics.test.php
+++ b/cake/tests/cases/basics.test.php
@@ -585,7 +585,7 @@ class BasicsTest extends CakeTestCase {
 		ob_start();
 			debug('this-is-a-test');
 		$result = ob_get_clean();
-		$pattern = '/.*\>(.+?tests(\/|\\\)cases(\/|\\\)basics\.test\.php|';
+		$pattern = '/(.+?tests(\/|\\\)cases(\/|\\\)basics\.test\.php|';
 		$pattern .= preg_quote(substr(__FILE__, 1), '/') . ')';
 		$pattern .= '.*line.*' . (__LINE__ - 4) . '.*this-is-a-test.*/s';
 		$this->assertPattern($pattern, $result);
@@ -593,7 +593,7 @@ class BasicsTest extends CakeTestCase {
 		ob_start();
 			debug('<div>this-is-a-test</div>', true);
 		$result = ob_get_clean();
-		$pattern = '/.*\>(.+?tests(\/|\\\)cases(\/|\\\)basics\.test\.php|';
+		$pattern = '/(.+?tests(\/|\\\)cases(\/|\\\)basics\.test\.php|';
 		$pattern .= preg_quote(substr(__FILE__, 1), '/') . ')';
 		$pattern .=	'.*line.*' . (__LINE__ - 4) . '.*&lt;div&gt;this-is-a-test&lt;\/div&gt;.*/s';
 		$this->assertPattern($pattern, $result);
diff --git a/cake/tests/cases/console/all_console.test.php b/cake/tests/cases/console/all_console.test.php
new file mode 100644
index 000000000..101a8faba
--- /dev/null
+++ b/cake/tests/cases/console/all_console.test.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * AllConsoleTest file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * AllConsoleTest class
+ *
+ * This test group will run all console classes.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ */
+class AllConsoleTest extends PHPUnit_Framework_TestSuite {
+
+/**
+ * suite method, defines tests for this suite.
+ *
+ * @return void
+ */
+	public static function suite() {
+		$suite = new CakeTestSuite('All console classes');
+
+		$path = CORE_TEST_CASES . DS . 'console' . DS;
+
+		$suite->addTestFile($path . 'all_console_libs.test.php');
+		$suite->addTestFile($path . 'all_shells.test.php');
+		$suite->addTestFile($path . 'all_tasks.test.php');
+		return $suite;
+	}
+}
\ No newline at end of file
diff --git a/cake/tests/cases/console/all_console_libs.test.php b/cake/tests/cases/console/all_console_libs.test.php
new file mode 100644
index 000000000..70fb8746d
--- /dev/null
+++ b/cake/tests/cases/console/all_console_libs.test.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * AllConsoleLibsTest file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
+ * @link          http://cakephp.org CakePHP(tm) Project
+ * @package       cake
+ * @subpackage    cake.tests.cases
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+/**
+ * AllConsoleLibsTest class
+ *
+ * This test group will run all console lib classes.
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ */
+class AllConsoleLibsTest extends PHPUnit_Framework_TestSuite {
+
+/**
+ * suite method, defines tests for this suite.
+ *
+ * @return void
+ */
+	public static function suite() {
+		$suite = new CakeTestSuite('All console lib classes');
+
+		$path = CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS;
+
+		$suite->addTestDirectory($path);
+		return $suite;
+	}
+}
\ No newline at end of file
diff --git a/cake/tests/cases/console/all_shells.test.php b/cake/tests/cases/console/all_shells.test.php
index 98afe5ad7..7f657af82 100644
--- a/cake/tests/cases/console/all_shells.test.php
+++ b/cake/tests/cases/console/all_shells.test.php
@@ -36,10 +36,8 @@ class AllShellsTest extends PHPUnit_Framework_TestSuite {
 	public static function suite() {
 		$suite = new CakeTestSuite('All shell classes');
 
-		$path = CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS;
+		$path = CORE_TEST_CASES . DS . 'console' . DS . 'shells' . DS;
 
-		$suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'cake.test.php');
-		$suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'console_error_handler.test.php');
 		$suite->addTestDirectory($path);
 		return $suite;
 	}
diff --git a/cake/tests/cases/console/all_tasks.test.php b/cake/tests/cases/console/all_tasks.test.php
index 7316f490e..51decf3d1 100644
--- a/cake/tests/cases/console/all_tasks.test.php
+++ b/cake/tests/cases/console/all_tasks.test.php
@@ -36,9 +36,7 @@ class AllTasksTest extends PHPUnit_Framework_TestSuite {
 	public static function suite() {
 		$suite = new CakeTestSuite('All Tasks tests');
 
-		$path = CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'tasks' . DS;
-
-		$suite->addTestFile(CORE_TEST_CASES . DS . 'console' . DS . 'libs' . DS . 'bake.test.php');
+		$path = CORE_TEST_CASES . DS . 'console' . DS . 'shells' . DS . 'tasks' . DS;
 		$suite->addTestDirectory($path);
 		return $suite;
 	}
diff --git a/cake/tests/cases/console/cake.test.php b/cake/tests/cases/console/cake.test.php
deleted file mode 100644
index 48afb4d6b..000000000
--- a/cake/tests/cases/console/cake.test.php
+++ /dev/null
@@ -1,932 +0,0 @@
-<?php
-/**
- * ShellDispatcherTest file
- *
- * PHP 5
- *
- * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
- * Copyright 2005-2010, Cake Software Foundation, Inc.
- *
- * Licensed under The MIT License
- * Redistributions of files must retain the above copyright notice
- *
- * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc.
- * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
- * @package       cake
- * @subpackage    cake.tests.cases.console
- * @since         CakePHP(tm) v 1.2.0.5432
- * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
- */
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CONSOLE_LIBS . 'shell.php';
-
-/**
- * TestShellDispatcher class
- *
- * @package       cake
- * @subpackage    cake.tests.cases.console
- */
-class TestShellDispatcher extends ShellDispatcher {
-
-/**
- * params property
- *
- * @var array
- * @access public
- */
-	public $params = array();
-
-/**
- * stdout property
- *
- * @var string
- * @access public
- */
-	public $stdout = '';
-
-/**
- * stderr property
- *
- * @var string
- * @access public
- */
-	public $stderr = '';
-
-/**
- * stopped property
- *
- * @var string
- * @access public
- */
-	public $stopped = null;
-
-/**
- * TestShell
- *
- * @var mixed
- * @access public
- */
-	public $TestShell;
-
-/**
- * _initEnvironment method
- *
- * @return void
- */
-	protected function _initEnvironment() {
-	}
-
-/**
- * stderr method
- *
- * @return void
- */
-	public function stderr($string) {
-		$this->stderr .= rtrim($string, ' ');
-	}
-
-/**
- * stdout method
- *
- * @return void
- */
-	public function stdout($string, $newline = true) {
-		if ($newline) {
-			$this->stdout .= rtrim($string, ' ') . "\n";
-		} else {
-			$this->stdout .= rtrim($string, ' ');
-		}
-	}
-
-/**
- * clear method
- *
- * @return void
- */
-	public function clear() {
-
-	}
-
-/**
- * _stop method
- *
- * @return void
- */
-	protected function _stop($status = 0) {
-		$this->stopped = 'Stopped with status: ' . $status;
-		return $status;
-	}
-
-/**
- * getShell
- *
- * @param mixed $plugin
- * @return mixed
- */
-	public function getShell($plugin = null) {
-		return $this->_getShell($plugin);
-	}
-
-/**
- * _getShell
- *
- * @param mixed $plugin
- * @return mixed
- */
-	protected function _getShell($plugin = null) {
-		if (isset($this->TestShell)) {
-			return $this->TestShell;
-		}
-		return parent::_getShell($plugin);
-	}
-}
-
-/**
- * ShellDispatcherTest
- *
- * @package       cake
- * @subpackage    cake.tests.cases.libs
- */
-class ShellDispatcherTest extends CakeTestCase {
-
-/**
- * setUp method
- *
- * @return void
- */
-	public function setUp() {
-		parent::setUp();
-		App::build(array(
-			'plugins' => array(
-				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS
-			),
-			'shells' => array(
-				CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS,
-				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'shells' . DS
-			)
-		), true);
-	}
-
-/**
- * testParseParams method
- *
- * @return void
- */
-	public function testParseParams() {
-		$Dispatcher = new TestShellDispatcher();
-
-		$params = array(
-			'/cake/1.2.x.x/cake/console/cake.php',
-			'bake',
-			'-app',
-			'new',
-			'-working',
-			'/var/www/htdocs'
-		);
-		$expected = array(
-			'app' => 'new',
-			'webroot' => 'webroot',
-			'working' => '/var/www/htdocs/new',
-			'root' => '/var/www/htdocs'
-		);
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array('cake.php');
-		$expected = array(
-			'app' => 'app',
-			'webroot' => 'webroot',
-			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'),
-			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'cake.php',
-			'-app',
-			'new',
-		);
-		$expected = array(
-			'app' => 'new',
-			'webroot' => 'webroot',
-			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
-			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'./cake.php',
-			'bake',
-			'-app',
-			'new',
-			'-working',
-			'/cake/1.2.x.x/cake/console'
-		);
-
-		$expected = array(
-			'app' => 'new',
-			'webroot' => 'webroot',
-			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
-			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
-		);
-
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'./console/cake.php',
-			'bake',
-			'-app',
-			'new',
-			'-working',
-			'/cake/1.2.x.x/cake'
-		);
-		$expected = array(
-			'app' => 'new',
-			'webroot' => 'webroot',
-			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
-			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'./console/cake.php',
-			'bake',
-			'-app',
-			'new',
-			'-dry',
-			'-working',
-			'/cake/1.2.x.x/cake'
-		);
-		$expected = array(
-			'app' => 'new',
-			'dry' => true,
-			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
-			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
-			'webroot' => 'webroot'
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEquals($expected, $Dispatcher->params);
-
-		$params = array(
-			'./console/cake.php',
-			'-working',
-			'/cake/1.2.x.x/cake',
-			'schema',
-			'run',
-			'create',
-			'-dry',
-			'-f',
-			'-name',
-			'DbAcl'
-		);
-		$expected = array(
-			'app' => 'app',
-			'webroot' => 'webroot',
-			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'),
-			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
-			'dry' => true,
-			'f' => true,
-			'name' => 'DbAcl'
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$expected = array('./console/cake.php', 'schema', 'run', 'create');
-		$this->assertEqual($expected, $Dispatcher->args);
-
-		$params = array(
-			'/cake/1.2.x.x/cake/console/cake.php',
-			'-working',
-			'/cake/1.2.x.x/app',
-			'schema',
-			'run',
-			'create',
-			'-dry',
-			'-name',
-			'DbAcl'
-		);
-		$expected = array(
-			'app' => 'app',
-			'webroot' => 'webroot',
-			'working' => '/cake/1.2.x.x/app',
-			'root' => '/cake/1.2.x.x',
-			'dry' => true,
-			'name' => 'DbAcl'
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$expected = array('/cake/1.2.x.x/cake/console/cake.php', 'schema', 'run', 'create');
-		$this->assertEqual($expected, $Dispatcher->args);
-		$params = array(
-			'cake.php',
-			'-working',
-			'C:/wamp/www/cake/app',
-			'bake',
-			'-app',
-			'C:/wamp/www/apps/cake/app',
-		);
-		$expected = array(
-			'app' => 'app',
-			'webroot' => 'webroot',
-			'working' => 'C:\wamp\www\apps\cake\app',
-			'root' => 'C:\wamp\www\apps\cake'
-		);
-
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'cake.php',
-			'-working',
-			'C:\wamp\www\cake\app',
-			'bake',
-			'-app',
-			'C:\wamp\www\apps\cake\app',
-		);
-		$expected = array(
-			'app' => 'app',
-			'webroot' => 'webroot',
-			'working' => 'C:\wamp\www\apps\cake\app',
-			'root' => 'C:\wamp\www\apps\cake'
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'cake.php',
-			'-working',
-			'C:\wamp\www\apps',
-			'bake',
-			'-app',
-			'cake\app',
-			'-url',
-			'http://example.com/some/url/with/a/path'
-		);
-		$expected = array(
-			'app' => 'app',
-			'webroot' => 'webroot',
-			'working' => 'C:\wamp\www\apps\cake\app',
-			'root' => 'C:\wamp\www\apps\cake',
-			'url' => 'http://example.com/some/url/with/a/path'
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'/home/amelo/dev/cake-common/cake/console/cake.php',
-			'-root',
-			'/home/amelo/dev/lsbu-vacancy',
-			'-working',
-			'/home/amelo/dev/lsbu-vacancy',
-			'-app',
-			'app',
-		);
-		$expected = array(
-			'app' => 'app',
-			'webroot' => 'webroot',
-			'working' => '/home/amelo/dev/lsbu-vacancy/app',
-			'root' => '/home/amelo/dev/lsbu-vacancy',
-		);
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-
-		$params = array(
-			'cake.php',
-			'-working',
-			'D:\www',
-			'bake',
-			'my_app',
-		);
-		$expected = array(
-			'working' => 'D:\www',
-			'app' => 'www',
-			'root' => 'D:',
-			'webroot' => 'webroot'
-		);
-
-		$Dispatcher->params = $Dispatcher->args = array();
-		$Dispatcher->parseParams($params);
-		$this->assertEqual($expected, $Dispatcher->params);
-	}
-
-/**
- * testBuildPaths method
- *
- * @return void
- */
-	public function testBuildPaths() {
-		$Dispatcher = new TestShellDispatcher();
-
-		$result = $Dispatcher->shellPaths;
-
-		$expected = array(
-			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'vendors' . DS . 'shells' . DS,
-			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin_two' . DS . 'vendors' . DS . 'shells' . DS,
-			APP . 'vendors' . DS . 'shells' . DS,
-			VENDORS . 'shells' . DS,
-			CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS,
-			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors' . DS . 'shells' . DS,
-		);
-		$this->assertIdentical(array_diff($result, $expected), array());
-		$this->assertIdentical(array_diff($expected, $result), array());
-	}
-
-/**
- * Verify loading of (plugin-) shells
- *
- * @return void
- */
-	public function testGetShell() {
-		$this->skipIf(class_exists('SampleShell'), '%s SampleShell Class already loaded');
-		$this->skipIf(class_exists('ExampleShell'), '%s ExampleShell Class already loaded');
-
-		$Dispatcher = new TestShellDispatcher();
-
-		$Dispatcher->shell = 'sample';
-		$Dispatcher->shellName = 'Sample';
-		$Dispatcher->shellClass = 'SampleShell';
-
-		$result = $Dispatcher->getShell();
-		$this->assertIsA($result, 'SampleShell');
-
-		$Dispatcher = new TestShellDispatcher();
-
-		$Dispatcher->shell = 'example';
-		$Dispatcher->shellName = 'Example';
-		$Dispatcher->shellClass = 'ExampleShell';
-
-		$result = $Dispatcher->getShell('test_plugin');
-		$this->assertIsA($result, 'ExampleShell');
-	}
-
-/**
- * Verify correct dispatch of Shell subclasses with a main method
- *
- * @return void
- */
-	public function testDispatchShellWithMain() {
-		$Dispatcher = new TestShellDispatcher();
-		$methods = get_class_methods('Shell');
-		array_push($methods, 'main', '_secret');
-		$Mock = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MockWithMainShell');
-
-		$Mock->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Mock->expects($this->once())->method('initialize');
-		$Mock->expects($this->once())->method('loadTasks');
-		$Mock->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Mock;
-
-		$Dispatcher->args = array('mock_with_main');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array());
-
-		$Shell = new MockWithMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main', 'initdb');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('initdb'));
-
-		$Shell = new MockWithMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('startup');
-		$Shell->expects($this->once())->method('help');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main', 'help');
-		$result = $Dispatcher->dispatch();
-		$this->assertNull($result);
-		$this->assertEqual($Dispatcher->args, array());
-
-		$Shell = new MockWithMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->never())->method('hr');
-		$Shell->expects($this->once())->method('startup');
-		$Shell->expects($this->once())->method('main');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main', 'hr');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('hr'));
-
-		$Shell = new MockWithMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main', 'dispatch');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('dispatch'));
-
-		$Shell = new MockWithMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main', 'idontexist');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('idontexist'));
-
-		$Shell = new MockWithMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->never())->method('main');
-		$Shell->expects($this->never())->method('startup');
-		$Shell->expects($this->never())->method('_secret');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main', '_secret');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-	}
-
-/**
- * Verify correct dispatch of Shell subclasses without a main method
- *
- * @return void
- */
-	public function testDispatchShellWithoutMain() {
-		$Dispatcher = new TestShellDispatcher();
-		$methods = get_class_methods('Shell');
-		array_push($methods, 'initDb', '_secret');
-		$Shell = $this->getMock('Shell', $methods, array(&$Dispatcher), 'MockWithoutMainShell');
-
-		$Shell->expects($this->once())->method('initialize');
-		$Shell->expects($this->once())->method('loadTasks');
-		$Shell->expects($this->never())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-		$this->assertEqual($Dispatcher->args, array());
-
-		$Shell = new MockWithoutMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('initDb')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('initialize');
-		$Shell->expects($this->once())->method('loadTasks');
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main', 'initdb');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array());
-
-		$Shell = new MockWithoutMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->never())->method('hr');
-		$Shell->expects($this->never())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main', 'hr');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-		$this->assertEqual($Dispatcher->args, array('hr'));
-
-		$Shell = new MockWithoutMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->never())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main', 'dispatch');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-
-		$Shell = new MockWithoutMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->never())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main', 'idontexist');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-
-		$Shell = new MockWithoutMainShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->never())->method('startup');
-		$Shell->expects($this->never())->method('_secret');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main', '_secret');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-	}
-
-/**
- * Verify correct dispatch of custom classes with a main method
- *
- * @return void
- */
-	public function testDispatchNotAShellWithMain() {
-		$Dispatcher = new TestShellDispatcher();
-		$methods = get_class_methods('Object');
-		array_push($methods, 'main', 'initdb', 'initialize', 'loadTasks', 'startup', '_secret');
-		$Shell = $this->getMock('Object', $methods, array(&$Dispatcher), 'MockWithMainNotAShell');
-
-		$Shell->expects($this->never())->method('initialize');
-		$Shell->expects($this->never())->method('loadTasks');
-		$Shell->expects($this->once())->method('startup');
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array());
-
-		$Shell = new MockWithMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('initdb')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a', 'initdb');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-
-		$Shell = new MockWithMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a', 'hr');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('hr'));
-
-
-		$Shell = new MockWithMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a', 'dispatch');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('dispatch'));
-
-		$Shell = new MockWithMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a', 'idontexist');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('idontexist'));
-
-		$Shell = new MockWithMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->never())->method('_secret');
-		$Shell->expects($this->never())->method('main');
-		$Shell->expects($this->never())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_with_main_not_a', '_secret');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-	}
-
-/**
- * Verify correct dispatch of custom classes without a main method
- *
- * @return void
- */
-	public function testDispatchNotAShellWithoutMain() {
-		$Dispatcher = new TestShellDispatcher();
-		$methods = get_class_methods('Object');
-		array_push($methods, 'main', 'initdb', 'initialize', 'loadTasks', 'startup', '_secret');
-		$Shell = $this->getMock('Object', $methods, array(&$Dispatcher), 'MockWithoutMainNotAShell');
-
-		$Shell->expects($this->never())->method('initialize');
-		$Shell->expects($this->never())->method('loadTasks');
-		$Shell->expects($this->once())->method('startup');
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main_not_a');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array());
-
-		$Shell = new MockWithoutMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('initdb')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main_not_a', 'initdb');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-
-		$Shell = new MockWithoutMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main_not_a', 'hr');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('hr'));
-
-
-		$Shell = new MockWithoutMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main_not_a', 'dispatch');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('dispatch'));
-
-		$Shell = new MockWithoutMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
-		$Shell->expects($this->once())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main_not_a', 'idontexist');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array('idontexist'));
-
-		$Shell = new MockWithoutMainNotAShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->never())->method('_secret');
-		$Shell->expects($this->never())->method('main');
-		$Shell->expects($this->never())->method('startup');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_without_main_not_a', '_secret');
-		$result = $Dispatcher->dispatch();
-		$this->assertFalse($result);
-	}
-
-/**
- * Verify that a task is called instead of the shell if the first arg equals
- * the name of the task
- *
- * @return void
- */
-	public function testDispatchTask() {
-		$Dispatcher = new TestShellDispatcher();
-		$mainMethods = $executeMethods = get_class_methods('Shell');
-		array_push($mainMethods, 'main');
-		array_push($executeMethods, 'execute');
-
-		$Week = $this->getMock('Shell', $mainMethods, array(&$Dispatcher), 'MockWeekShell');
-		$Sunday = $this->getMock('Shell', $executeMethods, array(&$Dispatcher), 'MockOnSundayTask');
-
-		$Shell = new MockWeekShell($Dispatcher);
-		$this->mockObjects[] = $Shell;
-		$Shell->expects($this->once())->method('initialize');
-		$Shell->expects($this->once())->method('loadTasks');
-		$Shell->expects($this->never())->method('startup');
-		$Shell->expects($this->never())->method('main');
-
-		$Task = new MockOnSundayTask($Dispatcher);
-		$this->mockObjects[] = $Task;
-		$Task->expects($this->once())->method('execute')->will($this->returnValue(true));
-		$Task->expects($this->once())->method('initialize');;
-		$Task->expects($this->once())->method('loadTasks');
-		$Task->expects($this->once())->method('startup');
-		$Task->expects($this->once())->method('execute');
-
-		$Shell->MockOnSunday = $Task;
-		$Shell->taskNames = array('MockOnSunday');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_week', 'mock_on_sunday');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-		$this->assertEqual($Dispatcher->args, array());
-
-		$Shell = new MockWeekShell($Dispatcher);
-		$Task = new MockOnSundayTask($Dispatcher);
-		array_push($this->mockObjects, $Shell, $Task);
-
-		$Task->expects($this->never())->method('execute');
-		$Task->expects($this->once())->method('help');
-
-		$Shell->MockOnSunday = $Task;
-		$Shell->taskNames = array('MockOnSunday');
-		$Dispatcher->TestShell = $Shell;
-
-		$Dispatcher->args = array('mock_week', 'mock_on_sunday', 'help');
-		$result = $Dispatcher->dispatch();
-		$this->assertTrue($result);
-	}
-
-/**
- * Verify shifting of arguments
- *
- * @return void
- */
-	public function testShiftArgs() {
-		$Dispatcher = new TestShellDispatcher();
-
-		$Dispatcher->args = array('a', 'b', 'c');
-		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
-		$this->assertIdentical($Dispatcher->args, array('b', 'c'));
-
-		$Dispatcher->args = array('a' => 'b', 'c', 'd');
-		$this->assertEqual($Dispatcher->shiftArgs(), 'b');
-		$this->assertIdentical($Dispatcher->args, array('c', 'd'));
-
-		$Dispatcher->args = array('a', 'b' => 'c', 'd');
-		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
-		$this->assertIdentical($Dispatcher->args, array('b' => 'c', 'd'));
-
-		$Dispatcher->args = array(0 => 'a',  2 => 'b', 30 => 'c');
-		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
-		$this->assertIdentical($Dispatcher->args, array(0 => 'b', 1 => 'c'));
-
-		$Dispatcher->args = array();
-		$this->assertNull($Dispatcher->shiftArgs());
-		$this->assertIdentical($Dispatcher->args, array());
-	}
-
-/**
- * testHelpCommand method
- *
- * @return void
- */
-	public function testHelpCommand() {
-		$Dispatcher = new TestShellDispatcher();
-
-		$expected = "/example \[.*TestPlugin, TestPluginTwo.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/welcome \[.*TestPluginTwo.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/acl \[.*CORE.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/api \[.*CORE.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/bake \[.*CORE.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/console \[.*CORE.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/i18n \[.*CORE.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/schema \[.*CORE.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/testsuite \[.*CORE.*\]/";
-	 	$this->assertPattern($expected, $Dispatcher->stdout);
-
-		$expected = "/sample \[.*test_app.*\]/";
-		$this->assertPattern($expected, $Dispatcher->stdout);
-	}
-}
diff --git a/cake/tests/cases/console/console_error_handler.test.php b/cake/tests/cases/console/libs/console_error_handler.test.php
similarity index 82%
rename from cake/tests/cases/console/console_error_handler.test.php
rename to cake/tests/cases/console/libs/console_error_handler.test.php
index d6e3b261e..81c3b5aae 100644
--- a/cake/tests/cases/console/console_error_handler.test.php
+++ b/cake/tests/cases/console/libs/console_error_handler.test.php
@@ -17,7 +17,7 @@
  * @since         CakePHP(tm) v 2.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-require_once CAKE . 'console' . DS . 'console_error_handler.php';
+require_once CONSOLE_LIBS . 'console_error_handler.php';
 
 /**
  * ConsoleErrorHandler Test case.
@@ -32,7 +32,9 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
  * @return Mock object
  */
 	function getErrorHandler($exception) {
-		return $this->getMock('ConsoleErrorHandler', array('stderr'), array($exception));
+		$error = new ConsoleErrorHandler($exception);
+		$error->stderr = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		return $error;
 	}
 
 /**
@@ -44,7 +46,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
 		$exception = new MissingActionException('Missing action');
 		$error = $this->getErrorHandler($exception);
 
-		$error->expects($this->once())->method('stderr')
+		$error->stderr->expects($this->once())->method('write')
 			->with($this->stringContains('Missing action'));
 
 		$error->render();
@@ -59,7 +61,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
 		$exception = new InvalidArgumentException('Too many parameters.');
 		$error = $this->getErrorHandler($exception);
 
-		$error->expects($this->once())->method('stderr')
+		$error->stderr->expects($this->once())->method('write')
 			->with($this->stringContains('Too many parameters.'));
 		
 		$error->render();
@@ -74,7 +76,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
 		$exception = new NotFoundException('dont use me in cli.');
 		$error = $this->getErrorHandler($exception);
 
-		$error->expects($this->once())->method('stderr')
+		$error->stderr->expects($this->once())->method('write')
 			->with($this->stringContains('dont use me in cli.'));
 
 		$error->render();
@@ -89,7 +91,7 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
 		$exception = new InternalErrorException('dont use me in cli.');
 		$error = $this->getErrorHandler($exception);
 
-		$error->expects($this->once())->method('stderr')
+		$error->stderr->expects($this->once())->method('write')
 			->with($this->stringContains('dont use me in cli.'));
 
 		$error->render();
@@ -104,6 +106,6 @@ class ConsoleErrorHandlerTest extends CakeTestCase {
 		$exception = new InternalErrorException('dont use me in cli.');
 		$error = new ConsoleErrorHandler($exception);
 
-		$this->assertTrue(is_resource($error->stderr), 'No handle.');
+		$this->assertType('ConsoleOutput', $error->stderr, 'No handle.');
 	}
 }
\ No newline at end of file
diff --git a/cake/tests/cases/console/libs/console_option_parser.test.php b/cake/tests/cases/console/libs/console_option_parser.test.php
new file mode 100644
index 000000000..2b46cd00b
--- /dev/null
+++ b/cake/tests/cases/console/libs/console_option_parser.test.php
@@ -0,0 +1,495 @@
+<?php
+/**
+ * ConsoleOptionParserTest file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2010, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ * @since         CakePHP(tm) v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+require_once CONSOLE_LIBS . 'console_option_parser.php';
+
+class ConsoleOptionParserTest extends CakeTestCase {
+
+/**
+ * test setting the console description
+ *
+ * @return void
+ */
+	function testDescription() {
+		$parser = new ConsoleOptionParser('test', false);
+		$result = $parser->description('A test');
+
+		$this->assertEquals($parser, $result, 'Setting description is not chainable');
+		$this->assertEquals('A test', $parser->description(), 'getting value is wrong.');
+
+		$result = $parser->description(array('A test', 'something'));
+		$this->assertEquals("A test\nsomething", $parser->description(), 'getting value is wrong.');
+	}
+
+/**
+ * test setting the console epliog
+ *
+ * @return void
+ */
+	function testEpilog() {
+		$parser = new ConsoleOptionParser('test', false);
+		$result = $parser->epilog('A test');
+
+		$this->assertEquals($parser, $result, 'Setting epilog is not chainable');
+		$this->assertEquals('A test', $parser->epilog(), 'getting value is wrong.');
+		
+		$result = $parser->epilog(array('A test', 'something'));
+		$this->assertEquals("A test\nsomething", $parser->epilog(), 'getting value is wrong.');
+	}
+
+/**
+ * test adding an option returns self.
+ *
+ * @return void
+ */
+	function testAddOptionReturnSelf() {
+		$parser = new ConsoleOptionParser('test', false);
+		$result = $parser->addOption('test');
+		$this->assertEquals($parser, $result, 'Did not return $this from addOption');
+	}
+
+/**
+ * test adding an option and using the long value for parsing.
+ *
+ * @return void
+ */
+	function testAddOptionLong() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test', array(
+			'short' => 't'
+		));
+		$result = $parser->parse(array('--test', 'value'));
+		$this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out');
+	}
+
+/**
+ * test adding an option and using the long value for parsing.
+ *
+ * @return void
+ */
+	function testAddOptionLongEquals() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test', array(
+			'short' => 't'
+		));
+		$result = $parser->parse(array('--test=value'));
+		$this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out');
+	}
+
+/**
+ * test adding an option and using the default.
+ *
+ * @return void
+ */
+	function testAddOptionDefault() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test', array(
+			'default' => 'default value',
+		));
+		$result = $parser->parse(array('--test'));
+		$this->assertEquals(array('test' => 'default value', 'help' => false), $result[0], 'Default value did not parse out');
+		
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test', array(
+			'default' => 'default value',
+		));
+		$result = $parser->parse(array());
+		$this->assertEquals(array('test' => 'default value', 'help' => false), $result[0], 'Default value did not parse out');
+	}
+
+/**
+ * test adding an option and using the short value for parsing.
+ *
+ * @return void
+ */
+	function testAddOptionShort() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test', array(
+			'short' => 't'
+		));
+		$result = $parser->parse(array('-t', 'value'));
+		$this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Short parameter did not parse out');
+	}
+
+/**
+ * test adding and using boolean options.
+ *
+ * @return void
+ */
+	function testAddOptionBoolean() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test', array(
+			'boolean' => true,
+		));
+
+		$result = $parser->parse(array('--test', 'value'));
+		$expected = array(array('test' => true, 'help' => false), array('value'));
+		$this->assertEquals($expected, $result);
+		
+		$result = $parser->parse(array('value'));
+		$expected = array(array('test' => false, 'help' => false), array('value'));
+		$this->assertEquals($expected, $result);
+	}
+
+/**
+ * test adding an multiple shorts.
+ *
+ * @return void
+ */
+	function testAddOptionMultipleShort() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test', array('short' => 't', 'boolean' => true))
+			->addOption('file', array('short' => 'f', 'boolean' => true))
+			->addOption('output', array('short' => 'o', 'boolean' => true));
+
+		$result = $parser->parse(array('-o', '-t', '-f'));
+		$expected = array('file' => true, 'test' => true, 'output' => true, 'help' => false);
+		$this->assertEquals($expected, $result[0], 'Short parameter did not parse out');
+
+		$result = $parser->parse(array('-otf'));
+		$this->assertEquals($expected, $result[0], 'Short parameter did not parse out');
+	}
+
+/**
+ * test multiple options at once.
+ *
+ * @return void
+ */
+	function testMultipleOptions() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('test')
+			->addOption('connection')
+			->addOption('table', array('short' => 't', 'default' => true));
+
+		$result = $parser->parse(array('--test', 'value', '-t', '--connection', 'postgres'));
+		$expected = array('test' => 'value', 'table' => true, 'connection' => 'postgres', 'help' => false);
+		$this->assertEquals($expected, $result[0], 'multiple options did not parse');
+	}
+
+/**
+ * Test adding multiple options.
+ *
+ * @return void
+ */
+	function testAddOptions() {
+		$parser = new ConsoleOptionParser('something', false);
+		$result = $parser->addOptions(array(
+			'name' => array('help' => 'The name'),
+			'other' => array('help' => 'The other arg')
+		));
+		$this->assertEquals($parser, $result, 'addOptions is not chainable.');
+
+		$result = $parser->options();
+		$this->assertEquals(3, count($result), 'Not enough options');
+	}
+
+/**
+ * test that boolean options work
+ *
+ * @return void
+ */
+	function testOptionWithBooleanParam() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('no-commit', array('boolean' => true))
+			->addOption('table', array('short' => 't'));
+
+		$result = $parser->parse(array('--table', 'posts', '--no-commit', 'arg1', 'arg2'));
+		$expected = array(array('table' => 'posts', 'no-commit' => true, 'help' => false), array('arg1', 'arg2'));
+		$this->assertEquals($expected, $result, 'Boolean option did not parse correctly.');
+	}
+
+/**
+ * test parsing options that do not exist.
+ *
+ * @expectedException InvalidArgumentException
+ */
+	function testOptionThatDoesNotExist() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('no-commit', array('boolean' => true));
+
+		$result = $parser->parse(array('--fail', 'other'));
+	}
+
+/**
+ * test that options with choices enforce them.
+ *
+ * @expectedException InvalidArgumentException
+ * @return void
+ */
+	function testOptionWithChoices() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('name', array('choices' => array('mark', 'jose')));
+		
+		$result = $parser->parse(array('--name', 'mark'));
+		$expected = array('name' => 'mark', 'help' => false);
+		$this->assertEquals($expected, $result[0], 'Got the correct value.');
+
+		$result = $parser->parse(array('--name', 'jimmy'));
+	}
+
+/**
+ * test positional argument parsing.
+ *
+ * @return void
+ */
+	function testPositionalArgument() {
+		$parser = new ConsoleOptionParser('test', false);
+		$result = $parser->addArgument('name', array('help' => 'An argument'));
+		$this->assertEquals($parser, $result, 'Should returnn this');
+	}
+
+/**
+ * test overwriting positional arguments.
+ *
+ * @return void
+ */
+	function testPositionalArgOverwrite() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addArgument('name', array('help' => 'An argument'))
+			->addArgument('other', array('index' => 0));
+
+		$result = $parser->arguments();
+		$this->assertEquals(1, count($result), 'Overwrite did not occur');
+	}
+
+/**
+ * test parsing arguments.
+ *
+ * @expectedException InvalidArgumentException
+ * @return void
+ */
+	function testParseArgumentTooMany() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addArgument('name', array('help' => 'An argument'))
+			->addArgument('other');
+
+		$expected = array('one', 'two');
+		$result = $parser->parse($expected);
+		$this->assertEquals($expected, $result[1], 'Arguments are not as expected');
+
+		$result = $parser->parse(array('one', 'two', 'three'));
+	}
+
+/**
+ * test that when there are not enough arguments an exception is raised
+ *
+ * @expectedException RuntimeException
+ * @return void
+ */
+	function testPositionalArgNotEnough() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addArgument('name', array('required' => true))
+			->addArgument('other', array('required' => true));
+
+		$parser->parse(array('one'));
+	}
+
+/**
+ * test that arguments with choices enforce them.
+ *
+ * @expectedException InvalidArgumentException
+ * @return void
+ */
+	function testPositionalArgWithChoices() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addArgument('name', array('choices' => array('mark', 'jose')))
+			->addArgument('alias', array('choices' => array('cowboy', 'samurai')))
+			->addArgument('weapon', array('choices' => array('gun', 'sword')));
+
+		$result = $parser->parse(array('mark', 'samurai', 'sword'));
+		$expected = array('mark', 'samurai', 'sword');
+		$this->assertEquals($expected, $result[1], 'Got the correct value.');
+
+		$result = $parser->parse(array('jose', 'coder'));
+	}
+
+/**
+ * Test adding multiple arguments.
+ *
+ * @return void
+ */
+	function testAddArguments() {
+		$parser = new ConsoleOptionParser('test', false);
+		$result = $parser->addArguments(array(
+			'name' => array('help' => 'The name'),
+			'other' => array('help' => 'The other arg')
+		));
+		$this->assertEquals($parser, $result, 'addArguments is not chainable.');
+
+		$result = $parser->arguments();
+		$this->assertEquals(2, count($result), 'Not enough arguments');
+	}
+
+/**
+ * test setting a subcommand up.
+ *
+ * @return void
+ */
+	function testSubcommand() {
+		$parser = new ConsoleOptionParser('test', false);
+		$result = $parser->addSubcommand('initdb', array(
+			'help' => 'Initialize the database'
+		));
+		$this->assertEquals($parser, $result, 'Adding a subcommand is not chainable');
+	}
+
+/**
+ * test adding multiple subcommands
+ *
+ * @return void
+ */
+	function testAddSubcommands() {
+		$parser = new ConsoleOptionParser('test', false);
+		$result = $parser->addSubcommands(array(
+			'initdb' => array('help' => 'Initialize the database'),
+			'create' => array('help' => 'Create something')
+		));
+		$this->assertEquals($parser, $result, 'Adding a subcommands is not chainable');
+		$result = $parser->subcommands();
+		$this->assertEquals(2, count($result), 'Not enough subcommands');
+	}
+
+/**
+ * test that no exception is triggered when help is being generated
+ *
+ * @return void
+ */
+	function testHelpNoExceptionWhenGettingHelp() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.'))
+			->addArgument('model', array('help' => 'The model to make.', 'required' => true));
+
+		$result = $parser->parse(array('--help'));
+		$this->assertTrue($result[0]['help']);
+	}
+
+/**
+ * test that help() with a command param shows the help for a subcommand
+ *
+ * @return void
+ */
+	function testHelpSubcommandHelp() {
+		$subParser = new ConsoleOptionParser('method', false);
+		$subParser->addOption('connection', array('help' => 'Db connection.'));
+
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addSubcommand('method', array(
+				'help' => 'This is another command',
+				'parser' => $subParser
+			))
+			->addOption('test', array('help' => 'A test option.'));
+
+		$result = $parser->help('method');
+		$expected = <<<TEXT
+<info>Usage:</info>
+cake mycommand method [-h] [--connection]
+
+<info>Options:</info>
+
+--help, -h        Display this help.
+--connection      Db connection.
+
+TEXT;
+		$this->assertEquals($expected, $result, 'Help is not correct.');
+	}
+
+/**
+ * test building a parser from an array.
+ *
+ * @return void
+ */
+	function testBuildFromArray() {
+		$spec = array(
+			'command' => 'test',
+			'arguments' => array(
+				'name' => array('help' => 'The name'),
+				'other' => array('help' => 'The other arg')
+			),
+			'options' => array(
+				'name' => array('help' => 'The name'),
+				'other' => array('help' => 'The other arg')
+			),
+			'subcommands' => array(
+				'initdb' => array('help' => 'make database')
+			),
+			'description' => 'description text',
+			'epilog' => 'epilog text'
+		);
+		$parser = ConsoleOptionParser::buildFromArray($spec);
+
+		$this->assertEquals($spec['description'], $parser->description());
+		$this->assertEquals($spec['epilog'], $parser->epilog());
+
+		$options = $parser->options();
+		$this->assertTrue(isset($options['name']));
+		$this->assertTrue(isset($options['other']));
+
+		$args = $parser->arguments();
+		$this->assertEquals(2, count($args));
+		
+		$commands = $parser->subcommands();
+		$this->assertEquals(1, count($commands));
+	}
+
+/**
+ * test that create() returns instances
+ *
+ * @return void
+ */
+	function testCreateFactory() {
+		$parser = ConsoleOptionParser::create('factory', false);
+		$this->assertInstanceOf('ConsoleOptionParser', $parser);
+		$this->assertEquals('factory', $parser->command());
+	}
+
+/**
+ * test that parse() takes a subcommand argument, and that the subcommand parser
+ * is used.
+ *
+ * @return void
+ */
+	function testParsingWithSubParser() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->addOption('primary')
+			->addArgument('one', array('required' => true, 'choices' => array('a', 'b')))
+			->addArgument('two', array('required' => true))
+			->addSubcommand('sub', array(
+				'parser' => array(
+					'options' => array(
+						'secondary' => array('boolean' => true),
+						'fourth' => array('help' => 'fourth option')
+					),
+					'arguments' => array(
+						'sub_arg' => array('choices' => array('c', 'd'))
+					)
+				)
+			));
+		
+		$result = $parser->parse(array('--secondary', '--fourth', '4', 'c'), 'sub');
+		$expected = array(array(
+			'secondary' => true,
+			'fourth' => '4',
+			'help' => false,
+			'verbose' => false,
+			'quiet' => false), array('c'));
+		$this->assertEquals($expected, $result, 'Sub parser did not parse request.');
+	}
+
+}
\ No newline at end of file
diff --git a/cake/tests/cases/console/libs/console_output.test.php b/cake/tests/cases/console/libs/console_output.test.php
new file mode 100644
index 000000000..37fee8f5c
--- /dev/null
+++ b/cake/tests/cases/console/libs/console_output.test.php
@@ -0,0 +1,228 @@
+<?php
+/**
+ * ConsoleOutputTest file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2010, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+
+require_once CONSOLE_LIBS . 'console_output.php';
+
+class ConsoleOutputTest extends CakeTestCase {
+
+/**
+ * setup
+ *
+ * @return void
+ */
+	function setUp() {
+		parent::setUp();
+		$this->output = $this->getMock('ConsoleOutput', array('_write'));
+	}
+
+/**
+ * tearDown
+ *
+ * @return void
+ */
+	function tearDown() {
+		unset($this->output);
+	}
+
+/**
+ * test writing with no new line
+ *
+ * @return void
+ */
+	function testWriteNoNewLine() {
+		$this->output->expects($this->once())->method('_write')
+			->with('Some output');
+
+		$this->output->write('Some output', false);
+	}
+
+/**
+ * test writing with no new line
+ *
+ * @return void
+ */
+	function testWriteNewLine() {
+		$this->output->expects($this->once())->method('_write')
+			->with('Some output' . PHP_EOL);
+
+		$this->output->write('Some output');
+	}
+
+/**
+ * test write() with multiple new lines
+ *
+ * @return void
+ */
+	function testWriteMultipleNewLines() {
+		$this->output->expects($this->once())->method('_write')
+			->with('Some output' . PHP_EOL . PHP_EOL . PHP_EOL . PHP_EOL);
+
+		$this->output->write('Some output', 4);
+	}
+
+/**
+ * test writing an array of messages.
+ *
+ * @return void
+ */
+	function testWriteArray() {
+		$this->output->expects($this->once())->method('_write')
+			->with('Line' . PHP_EOL . 'Line' . PHP_EOL . 'Line' . PHP_EOL);
+
+		$this->output->write(array('Line', 'Line', 'Line'));
+	}
+
+/**
+ * test getting a style.
+ *
+ * @return void
+ */
+	function testStylesGet() {
+		$result = $this->output->styles('error');
+		$expected = array('text' => 'red', 'underline' => true);
+		$this->assertEqual($result, $expected);
+
+		$this->assertNull($this->output->styles('made_up_goop'));
+
+		$result = $this->output->styles();
+		$this->assertNotEmpty($result, 'error', 'Error is missing');
+		$this->assertNotEmpty($result, 'warning', 'Warning is missing');
+	}
+
+/**
+ * test adding a style.
+ *
+ * @return void
+ */
+	function testStylesAdding() {
+		$this->output->styles('test', array('text' => 'red', 'background' => 'black'));
+		$result = $this->output->styles('test');
+		$expected = array('text' => 'red', 'background' => 'black');
+		$this->assertEquals($expected, $result);
+		
+		$this->assertTrue($this->output->styles('test', false), 'Removing a style should return true.');
+		$this->assertNull($this->output->styles('test'), 'Removed styles should be null.');
+	}
+
+/**
+ * test formatting text with styles.
+ *
+ * @return void
+ */
+	function testFormattingSimple() {
+		$this->output->expects($this->once())->method('_write')
+			->with("\033[31;4mError:\033[0m Something bad");
+
+		$this->output->write('<error>Error:</error> Something bad', false);
+	}
+
+/**
+ * test that formatting doesn't eat tags it doesn't know about.
+ *
+ * @return void
+ */
+	function testFormattingNotEatingTags() {
+		$this->output->expects($this->once())->method('_write')
+			->with("<red> Something bad");
+
+		$this->output->write('<red> Something bad', false);
+	}
+
+/**
+ * test formatting with custom styles.
+ *
+ * @return void
+ */
+	function testFormattingCustom() {
+		$this->output->styles('annoying', array(
+			'text' => 'magenta',
+			'background' => 'cyan',
+			'blink' => true,
+			'underline' => true
+		));
+
+		$this->output->expects($this->once())->method('_write')
+			->with("\033[35;46;5;4mAnnoy:\033[0m Something bad");
+
+		$this->output->write('<annoying>Annoy:</annoying> Something bad', false);
+	}
+
+/**
+ * test formatting text with missing styles.
+ *
+ * @return void
+ */
+	function testFormattingMissingStyleName() {
+		$this->output->expects($this->once())->method('_write')
+			->with("<not_there>Error:</not_there> Something bad");
+
+		$this->output->write('<not_there>Error:</not_there> Something bad', false);
+	}
+
+/**
+ * test formatting text with multiple styles.
+ *
+ * @return void
+ */
+	function testFormattingMultipleStylesName() {
+		$this->output->expects($this->once())->method('_write')
+			->with("\033[31;4mBad\033[0m \033[33mWarning\033[0m Regular");
+
+		$this->output->write('<error>Bad</error> <warning>Warning</warning> Regular', false);
+	}
+
+/**
+ * test that multiple tags of the same name work in one string.
+ *
+ * @return void
+ */
+	function testFormattingMultipleSameTags() {
+		$this->output->expects($this->once())->method('_write')
+			->with("\033[31;4mBad\033[0m \033[31;4mWarning\033[0m Regular");
+
+		$this->output->write('<error>Bad</error> <error>Warning</error> Regular', false);
+	}
+
+/**
+ * test raw output not getting tags replaced.
+ *
+ * @return void
+ */
+	function testOutputAsRaw() {
+		$this->output->outputAs(ConsoleOutput::RAW);
+		$this->output->expects($this->once())->method('_write')
+			->with('<error>Bad</error> Regular');
+
+		$this->output->write('<error>Bad</error> Regular', false);
+	}
+
+/**
+ * test plain output.
+ *
+ * @return void
+ */
+	function testOutputAsPlain() {
+		$this->output->outputAs(ConsoleOutput::PLAIN);
+		$this->output->expects($this->once())->method('_write')
+			->with('Bad Regular');
+
+		$this->output->write('<error>Bad</error> Regular', false);
+	}
+}
\ No newline at end of file
diff --git a/cake/tests/cases/console/libs/help_formatter.test.php b/cake/tests/cases/console/libs/help_formatter.test.php
new file mode 100644
index 000000000..a957843b1
--- /dev/null
+++ b/cake/tests/cases/console/libs/help_formatter.test.php
@@ -0,0 +1,440 @@
+<?php
+
+require_once CONSOLE_LIBS . 'console_option_parser.php';
+require_once CONSOLE_LIBS . 'help_formatter.php';
+
+class HelpFormatterTest extends CakeTestCase {
+
+/**
+ * test that the console max width is respected when generating help.
+ *
+ * @return void
+ */
+	function testWidthFormatting() {
+		$parser = new ConsoleOptionParser('test', false);
+		$parser->description(__('This is fifteen This is fifteen This is fifteen'))
+			->addOption('four', array('help' => 'this is help text this is help text'))
+			->addArgument('four', array('help' => 'this is help text this is help text'))
+			->addSubcommand('four', array('help' => 'this is help text this is help text'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->text(30);
+		$expected = <<<TEXT
+This is fifteen This is
+fifteen This is fifteen
+
+<info>Usage:</info>
+cake test [subcommand] [-h] [--four] [<four>]
+
+<info>Subcommands:</info>
+
+four  this is help text this
+      is help text
+
+To see help on a subcommand use <info>`cake test [subcommand] --help`</info>
+
+<info>Options:</info>
+
+--help, -h  Display this help.
+--four      this is help text
+            this is help text
+
+<info>Arguments:</info>
+
+four  this is help text this
+      is help text
+      <comment>(optional)</comment>
+
+TEXT;
+		$this->assertEquals($expected, $result, 'Generated help is too wide');
+	}
+
+/**
+ * test help() with options and arguments that have choices.
+ *
+ * @return void
+ */
+	function testHelpWithChoices() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.', 'choices' => array('one', 'two')))
+			->addArgument('type', array(
+				'help' => 'Resource type.',
+				'choices' => array('aco', 'aro'),
+				'required' => true
+			))
+			->addArgument('other_longer', array('help' => 'Another argument.'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->text();
+		$expected = <<<TEXT
+<info>Usage:</info>
+cake mycommand [-h] [--test one|two] <aco|aro> [<other_longer>]
+
+<info>Options:</info>
+
+--help, -h  Display this help.
+--test      A test option. <comment>(choices: one|two)</comment>
+
+<info>Arguments:</info>
+
+type          Resource type. <comment>(choices: aco|aro)</comment>
+other_longer  Another argument. <comment>(optional)</comment>
+
+TEXT;
+		$this->assertEquals($expected, $result, 'Help does not match');
+	}
+
+/**
+ * test description and epilog in the help
+ *
+ * @return void
+ */
+	function testHelpDescriptionAndEpilog() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->description('Description text')
+			->epilog('epilog text')
+			->addOption('test', array('help' => 'A test option.'))
+			->addArgument('model', array('help' => 'The model to make.', 'required' => true));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->text();
+		$expected = <<<TEXT
+Description text
+
+<info>Usage:</info>
+cake mycommand [-h] [--test] <model>
+
+<info>Options:</info>
+
+--help, -h  Display this help.
+--test      A test option.
+
+<info>Arguments:</info>
+
+model  The model to make.
+
+epilog text
+
+TEXT;
+		$this->assertEquals($expected, $result, 'Help is wrong.');
+	}
+
+/**
+ * test that help() outputs subcommands.
+ *
+ * @return void
+ */
+	function testHelpSubcommand() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addSubcommand('method', array('help' => 'This is another command'))
+			->addOption('test', array('help' => 'A test option.'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->text();
+		$expected = <<<TEXT
+<info>Usage:</info>
+cake mycommand [subcommand] [-h] [--test]
+
+<info>Subcommands:</info>
+
+method  This is another command
+
+To see help on a subcommand use <info>`cake mycommand [subcommand] --help`</info>
+
+<info>Options:</info>
+
+--help, -h  Display this help.
+--test      A test option.
+
+TEXT;
+		$this->assertEquals($expected, $result, 'Help is not correct.');
+	}
+
+/**
+ * test getting help with defined options.
+ *
+ * @return void
+ */
+	function testHelpWithOptions() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.'))
+			->addOption('connection', array(
+				'short' => 'c', 'help' => 'The connection to use.', 'default' => 'default'
+			));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->text();
+		$expected = <<<TEXT
+<info>Usage:</info>
+cake mycommand [-h] [--test] [-c default]
+
+<info>Options:</info>
+
+--help, -h        Display this help.
+--test            A test option.
+--connection, -c  The connection to use. <comment>(default:
+                  default)</comment>
+
+TEXT;
+		$this->assertEquals($expected, $result, 'Help does not match');
+	}
+
+/**
+ * test getting help with defined options.
+ *
+ * @return void
+ */
+	function testHelpWithOptionsAndArguments() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.'))
+			->addArgument('model', array('help' => 'The model to make.', 'required' => true))
+			->addArgument('other_longer', array('help' => 'Another argument.'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->text();
+		$expected = <<<TEXT
+<info>Usage:</info>
+cake mycommand [-h] [--test] <model> [<other_longer>]
+
+<info>Options:</info>
+
+--help, -h  Display this help.
+--test      A test option.
+
+<info>Arguments:</info>
+
+model         The model to make.
+other_longer  Another argument. <comment>(optional)</comment>
+
+TEXT;
+		$this->assertEquals($expected, $result, 'Help does not match');
+	}
+
+/**
+ * test help() with options and arguments that have choices.
+ *
+ * @return void
+ */
+	function testXmlHelpWithChoices() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.', 'choices' => array('one', 'two')))
+			->addArgument('type', array(
+				'help' => 'Resource type.',
+				'choices' => array('aco', 'aro'),
+				'required' => true
+			))
+			->addArgument('other_longer', array('help' => 'Another argument.'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->xml();
+		$expected = <<<TEXT
+<?xml version="1.0"?>
+<shell>
+<name>mycommand</name>
+<description>Description text</description>
+<subcommands />
+<options>
+	<option name="--help" short="-h" help="Display this help." boolean="1">
+		<default></default>
+		<choices></choices>
+	</option>
+	<option name="--test" short="" help="A test option." boolean="0">
+		<default></default>
+		<choices>
+			<choice>one</choice>
+			<choice>two</choice>
+		</choices>
+	</option>
+</options>
+<arguments>
+	<argument name="type" help="Resource type." required="1">
+		<choices>
+			<choice>aco</choice>
+			<choice>aro</choice>
+		</choices>
+	</argument>
+</arguments>
+<epilog>epilog text</epilog>
+</shell>
+TEXT;
+		$this->assertEquals(new DomDocument($expected), new DomDocument($result), 'Help does not match');
+	}
+
+/**
+ * test description and epilog in the help
+ *
+ * @return void
+ */
+	function testXmlHelpDescriptionAndEpilog() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->description('Description text')
+			->epilog('epilog text')
+			->addOption('test', array('help' => 'A test option.'))
+			->addArgument('model', array('help' => 'The model to make.', 'required' => true));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->xml();
+		$expected = <<<TEXT
+<?xml version="1.0"?>
+<shell>
+<name>mycommand</name>
+<description>Description text</description>
+<subcommands />
+<options>
+	<option name="--help" short="-h" help="Display this help." boolean="1">
+		<default></default>
+		<choices></choices>
+	</option>
+	<option name="--test" short="" help="A test option." boolean="0">
+		<default></default>
+		<choices></choices>
+	</option>
+</options>
+<arguments>
+	<argument name="model" help="The model to make." required="1">
+		<choices></choices>
+	</argument>
+</arguments>
+<epilog>epilog text</epilog>
+</shell>
+TEXT;
+		$this->assertEquals(new DomDocument($expected), new DomDocument($result), 'Help does not match');
+	}
+
+/**
+ * test that help() outputs subcommands.
+ *
+ * @return void
+ */
+	function testXmlHelpSubcommand() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addSubcommand('method', array('help' => 'This is another command'))
+			->addOption('test', array('help' => 'A test option.'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->xml();
+		$expected = <<<TEXT
+<?xml version="1.0"?>
+<shell>
+<name>mycommand</name>
+<description/>
+<subcommands>
+	<command name="method" help="This is another command" />
+</subcommands>
+<options>
+	<option name="--help" short="-h" help="Display this help." boolean="1">
+		<default></default>
+		<choices></choices>
+	</option>
+	<option name="--test" short="" help="A test option." boolean="0">
+		<default></default>
+		<choices></choices>
+	</option>
+</options>
+<arguments/>
+<epilog/>
+</shell>
+TEXT;
+		$this->assertEquals(new DomDocument($expected), new DomDocument($result), 'Help does not match');
+	}
+
+/**
+ * test getting help with defined options.
+ *
+ * @return void
+ */
+	function testXmlHelpWithOptions() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.'))
+			->addOption('connection', array(
+				'short' => 'c', 'help' => 'The connection to use.', 'default' => 'default'
+			));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->xml();
+		$expected = <<<TEXT
+<?xml version="1.0"?>
+<shell>
+<name>mycommand</name>
+<description/>
+<subcommands/>
+<options>
+	<option name="--help" short="-h" help="Display this help." boolean="1">
+		<default></default>
+		<choices></choices>
+	</option>
+	<option name="--test" short="" help="A test option." boolean="0">
+		<default></default>
+		<choices></choices>
+	</option>
+	<option name="--connection" short="-c" help="The connection to use." boolean="0">
+		<default>default</default>
+		<choices></choices>
+	</option>
+</options>
+<arguments/>
+<epilog/>
+</shell>
+TEXT;
+		$this->assertEquals(new DomDocument($expected), new DomDocument($result), 'Help does not match');
+	}
+
+/**
+ * test getting help with defined options.
+ *
+ * @return void
+ */
+	function testXmlHelpWithOptionsAndArguments() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.'))
+			->addArgument('model', array('help' => 'The model to make.', 'required' => true))
+			->addArgument('other_longer', array('help' => 'Another argument.'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->xml();
+		$expected = <<<TEXT
+<?xml version="1.0"?>
+<shell>
+	<name>mycommand</name>
+	<description/>
+	<subcommands/>
+	<options>
+		<option name="--help" short="-h" help="Display this help." boolean="1">
+			<default></default>
+			<choices></choices>
+		</option>
+		<option name="--test" short="" help="A test option." boolean="0">
+			<default></default>
+			<choices></choices>
+		</option>
+	</options>
+	<arguments>
+		<argument name="model" help="The model to make." required="1">
+			<choices></choices>
+		</argument>
+		<argument name="other_longer" help="Another argument." required="0">
+			<choices></choices>
+		</argument>
+	</arguments>
+	<epilog/>
+</shell>
+TEXT;
+		$this->assertEquals(new DomDocument($expected), new DomDocument($result), 'Help does not match');
+	}
+
+/**
+ * Test xml help as object
+ *
+ * @return void
+ */
+	function testXmlHelpAsObject() {
+		$parser = new ConsoleOptionParser('mycommand', false);
+		$parser->addOption('test', array('help' => 'A test option.'))
+			->addArgument('model', array('help' => 'The model to make.', 'required' => true))
+			->addArgument('other_longer', array('help' => 'Another argument.'));
+
+		$formatter = new HelpFormatter($parser);
+		$result = $formatter->xml(false);
+		$this->assertType('SimpleXmlElement', $result);
+	}
+}
diff --git a/cake/tests/cases/console/libs/shell.test.php b/cake/tests/cases/console/libs/shell.test.php
index 157701079..f76d07623 100644
--- a/cake/tests/cases/console/libs/shell.test.php
+++ b/cake/tests/cases/console/libs/shell.test.php
@@ -22,18 +22,7 @@
 App::import('Core', 'Folder');
 App::import('Shell', 'Shell', false);
 
-
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * TestShell class
@@ -50,6 +39,7 @@ class TestShell extends Shell {
  * @access public
  */
 	public $name = 'TestShell';
+
 /**
  * stopped property
  *
@@ -67,6 +57,18 @@ class TestShell extends Shell {
 	protected function _stop($status = 0) {
 		$this->stopped = $status;
 	}
+
+	public function do_something() {
+		
+	}
+	
+	public function _secret() {
+		
+	}
+	
+	protected function no_access() {
+		
+	}
 }
 
 /**
@@ -114,21 +116,10 @@ class ShellTest extends CakeTestCase {
 	public function setUp() {
 		parent::setUp();
 
-		$this->Dispatcher = $this->getMock(
-			'ShellDispatcher',
-			array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear')
-		);
-		$this->Shell =& new TestShell($this->Dispatcher);
-	}
-
-/**
- * tearDown method
- *
- * @return void
- */
-	public function tearDown() {
-		parent::tearDown();
-		ClassRegistry::flush();
+		$output = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$error = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+		$this->Shell =& new TestShell($output, $error, $in);
 	}
 
 /**
@@ -137,9 +128,10 @@ class ShellTest extends CakeTestCase {
  * @return void
  */
 	public function testConstruct() {
-		$this->assertEquals($this->Dispatcher, $this->Shell->Dispatch);
 		$this->assertEqual($this->Shell->name, 'TestShell');
-		$this->assertEqual($this->Shell->alias, 'TestShell');
+		$this->assertType('ConsoleInput', $this->Shell->stdin);
+		$this->assertType('ConsoleOutput', $this->Shell->stdout);
+		$this->assertType('ConsoleOutput', $this->Shell->stderr);
 	}
 
 /**
@@ -166,11 +158,6 @@ class ShellTest extends CakeTestCase {
 		$this->assertIsA($this->Shell->Comment, 'Comment');
 		$this->assertEqual($this->Shell->modelClass, 'Comment');
 
-		$this->Shell->uses = true;
-		$this->Shell->initialize();
-		$this->assertTrue(isset($this->Shell->AppModel));
-		$this->assertIsA($this->Shell->AppModel, 'AppModel');
-
 		App::build();
 	}
 
@@ -180,29 +167,24 @@ class ShellTest extends CakeTestCase {
  * @return void
  */
 	public function testIn() {
-		$this->Dispatcher->expects($this->at(0))
-			->method('getInput')
-			->with('Just a test?', array('y', 'n'), 'n')
+		$this->Shell->stdin->expects($this->at(0))
+			->method('read')
 			->will($this->returnValue('n'));
 
-		$this->Dispatcher->expects($this->at(1))
-			->method('getInput')
-			->with('Just a test?', array('y', 'n'), 'n')
+		$this->Shell->stdin->expects($this->at(1))
+			->method('read')
 			->will($this->returnValue('Y'));
 
-		$this->Dispatcher->expects($this->at(2))
-			->method('getInput')
-			->with('Just a test?', 'y,n', 'n')
+		$this->Shell->stdin->expects($this->at(2))
+			->method('read')
 			->will($this->returnValue('y'));
 
-		$this->Dispatcher->expects($this->at(3))
-			->method('getInput')
-			->with('Just a test?', 'y/n', 'n')
+		$this->Shell->stdin->expects($this->at(3))
+			->method('read')
 			->will($this->returnValue('y'));
 
-		$this->Dispatcher->expects($this->at(4))
-			->method('getInput')
-			->with('Just a test?', 'y', 'y')
+		$this->Shell->stdin->expects($this->at(4))
+			->method('read')
 			->will($this->returnValue('y'));
 
 		$result = $this->Shell->in('Just a test?', array('y', 'n'), 'n');
@@ -232,21 +214,21 @@ class ShellTest extends CakeTestCase {
  * @return void
  */
 	public function testOut() {
-		$this->Shell->Dispatch->expects($this->at(0))
-			->method('stdout')
-			->with("Just a test\n", false);
+		$this->Shell->stdout->expects($this->at(0))
+			->method('write')
+			->with("Just a test", 1);
 
-		$this->Shell->Dispatch->expects($this->at(1))
-			->method('stdout')
-			->with("Just\na\ntest\n", false);
+		$this->Shell->stdout->expects($this->at(1))
+			->method('write')
+			->with(array('Just', 'a', 'test'), 1);
 
-		$this->Shell->Dispatch->expects($this->at(2))
-			->method('stdout')
-			->with("Just\na\ntest\n\n", false);
+		$this->Shell->stdout->expects($this->at(2))
+			->method('write')
+			->with(array('Just', 'a', 'test'), 2);
 
-		$this->Shell->Dispatch->expects($this->at(3))
-			->method('stdout')
-			->with("\n", false);
+		$this->Shell->stdout->expects($this->at(3))
+			->method('write')
+			->with('', 1);
 
 		$this->Shell->out('Just a test');
 
@@ -257,27 +239,65 @@ class ShellTest extends CakeTestCase {
 		$this->Shell->out();
 	}
 
+/**
+ * test that verbose and quiet output levels work
+ *
+ * @return void
+ */
+	function testVerboseOutput() {
+		$this->Shell->stdout->expects($this->at(0))->method('write')
+			->with('Verbose', 1);
+		$this->Shell->stdout->expects($this->at(1))->method('write')
+			->with('Normal', 1);
+		$this->Shell->stdout->expects($this->at(2))->method('write')
+			->with('Quiet', 1);
+
+		$this->Shell->params['verbose'] = true;
+		$this->Shell->params['quiet'] = false;
+
+		$this->Shell->out('Verbose', 1, Shell::VERBOSE);
+		$this->Shell->out('Normal', 1, Shell::NORMAL);
+		$this->Shell->out('Quiet', 1, Shell::QUIET);
+	}
+
+/**
+ * test that verbose and quiet output levels work
+ *
+ * @return void
+ */
+	function testQuietOutput() {
+		$this->Shell->stdout->expects($this->once())->method('write')
+			->with('Quiet', 1);
+
+		$this->Shell->params['verbose'] = false;
+		$this->Shell->params['quiet'] = true;
+
+		$this->Shell->out('Verbose', 1, Shell::VERBOSE);
+		$this->Shell->out('Normal', 1, Shell::NORMAL);
+		$this->Shell->out('Quiet', 1, Shell::QUIET);
+	}
+
 /**
  * testErr method
  *
  * @return void
  */
 	public function testErr() {
-		$this->Shell->Dispatch->expects($this->at(0))
-			->method('stderr')
-			->with("Just a test\n");
+		$this->Shell->stderr->expects($this->at(0))
+			->method('write')
+			->with("Just a test", 1);
 
-		$this->Shell->Dispatch->expects($this->at(1))
-			->method('stderr')
-			->with("Just\na\ntest\n");
+		$this->Shell->stderr->expects($this->at(1))
+			->method('write')
+			->with(array('Just', 'a', 'test'), 1);
 
-		$this->Shell->Dispatch->expects($this->at(2))
-			->method('stderr')
-			->with("Just\na\ntest\n\n");
+		$this->Shell->stderr->expects($this->at(2))
+			->method('write')
+			->with(array('Just', 'a', 'test'), 2);
 
-		$this->Shell->Dispatch->expects($this->at(3))
-			->method('stderr')
-			->with("\n");
+		$this->Shell->stderr->expects($this->at(3))
+			->method('write')
+			->with('', 1);
 
 		$this->Shell->err('Just a test');
 
@@ -309,17 +329,17 @@ class ShellTest extends CakeTestCase {
 	public function testHr() {
 		$bar = '---------------------------------------------------------------';
 
-		$this->Shell->Dispatch->expects($this->at(0))->method('stdout')->with('', false);
-		$this->Shell->Dispatch->expects($this->at(1))->method('stdout')->with($bar . "\n", false);
-		$this->Shell->Dispatch->expects($this->at(2))->method('stdout')->with('', false);
+		$this->Shell->stdout->expects($this->at(0))->method('write')->with('', 0);
+        $this->Shell->stdout->expects($this->at(1))->method('write')->with($bar, 1);
+        $this->Shell->stdout->expects($this->at(2))->method('write')->with('', 0);
 
-		$this->Shell->Dispatch->expects($this->at(3))->method('stdout')->with("\n", false);
-		$this->Shell->Dispatch->expects($this->at(4))->method('stdout')->with($bar . "\n", false);
-		$this->Shell->Dispatch->expects($this->at(5))->method('stdout')->with("\n", false);
+		$this->Shell->stdout->expects($this->at(3))->method('write')->with("", true);
+		$this->Shell->stdout->expects($this->at(4))->method('write')->with($bar, 1);
+		$this->Shell->stdout->expects($this->at(5))->method('write')->with("", true);
 
-		$this->Shell->Dispatch->expects($this->at(6))->method('stdout')->with("\n\n", false);
-		$this->Shell->Dispatch->expects($this->at(7))->method('stdout')->with($bar . "\n", false);
-		$this->Shell->Dispatch->expects($this->at(8))->method('stdout')->with("\n\n", false);
+		$this->Shell->stdout->expects($this->at(6))->method('write')->with("", 2);
+		$this->Shell->stdout->expects($this->at(7))->method('write')->with($bar, 1);
+		$this->Shell->stdout->expects($this->at(8))->method('write')->with("", 2);
 
 		$this->Shell->hr();
 
@@ -334,17 +354,17 @@ class ShellTest extends CakeTestCase {
  * @return void
  */
 	public function testError() {
-		$this->Shell->Dispatch->expects($this->at(0))
-			->method('stderr')
-			->with("Error: Foo Not Found\n");
+		$this->Shell->stderr->expects($this->at(0))
+			->method('write')
+			->with("<error>Error:</error> Foo Not Found", 1);
 
-		$this->Shell->Dispatch->expects($this->at(1))
-			->method('stderr')
-			->with("Error: Foo Not Found\n");
+		$this->Shell->stderr->expects($this->at(1))
+			->method('write')
+			->with("<error>Error:</error> Foo Not Found", 1);
 
-		$this->Shell->Dispatch->expects($this->at(2))
-			->method('stderr')
-			->with("Searched all...\n");
+		$this->Shell->stderr->expects($this->at(2))
+			->method('write')
+			->with("Searched all...", 1);
 
 		$this->Shell->error('Foo Not Found');
 		$this->assertIdentical($this->Shell->stopped, 1);
@@ -377,19 +397,37 @@ class ShellTest extends CakeTestCase {
 
 		$this->Shell->tasks = array('TestApple');
 		$this->assertTrue($this->Shell->loadTasks());
-		$this->assertIsA($this->Shell->TestApple, 'TestAppleTask');
+		$this->assertInstanceOf('TestAppleTask', $this->Shell->TestApple);
 
 		$this->Shell->tasks = 'TestBanana';
 		$this->assertTrue($this->Shell->loadTasks());
-		$this->assertIsA($this->Shell->TestApple, 'TestAppleTask');
-		$this->assertIsA($this->Shell->TestBanana, 'TestBananaTask');
+		$this->assertInstanceOf('TestAppleTask', $this->Shell->TestApple);
+		$this->assertInstanceOf('TestBananaTask', $this->Shell->TestBanana);
 
 		unset($this->Shell->ShellTestApple, $this->Shell->TestBanana);
 
 		$this->Shell->tasks = array('TestApple', 'TestBanana');
 		$this->assertTrue($this->Shell->loadTasks());
-		$this->assertIsA($this->Shell->TestApple, 'TestAppleTask');
-		$this->assertIsA($this->Shell->TestBanana, 'TestBananaTask');
+		$this->assertInstanceOf('TestAppleTask', $this->Shell->TestApple);
+		$this->assertInstanceOf('TestBananaTask', $this->Shell->TestBanana);
+	}
+
+/**
+ * test that __get() makes args and params references
+ *
+ * @return void
+ */
+	function test__getArgAndParamReferences() {
+		$this->Shell->tasks = array('TestApple');
+		$this->Shell->args = array('one');
+		$this->Shell->params = array('help' => false);
+		$this->Shell->loadTasks();
+		$result = $this->Shell->TestApple;
+		
+		$this->Shell->args = array('one', 'two');
+		
+		$this->assertSame($this->Shell->args, $result->args);
+		$this->assertSame($this->Shell->params, $result->params);
 	}
 
 /**
@@ -477,14 +515,12 @@ class ShellTest extends CakeTestCase {
 
 		$this->Shell->interactive = true;
 
-		$this->Shell->Dispatch->expects($this->at(5))
-			->method('getInput')
-			->withAnyParameters()
+		$this->Shell->stdin->expects($this->at(0))
+			->method('read')
 			->will($this->returnValue('n'));
 	
-		$this->Shell->Dispatch->expects($this->at(9))
-			->method('getInput')
-			->withAnyParameters()
+		$this->Shell->stdin->expects($this->at(1))
+			->method('read')
 			->will($this->returnValue('y'));
 
 
@@ -578,4 +614,161 @@ class ShellTest extends CakeTestCase {
 
 		$Folder->delete();
 	}
+
+/**
+ * test hasTask method
+ *
+ * @return void
+ */
+	function testHasTask() {
+		$this->Shell->tasks = array('Extract', 'DbConfig');
+		$this->Shell->loadTasks();
+		
+		$this->assertTrue($this->Shell->hasTask('extract'));
+		$this->assertTrue($this->Shell->hasTask('Extract'));
+		$this->assertFalse($this->Shell->hasTask('random'));
+		
+		$this->assertTrue($this->Shell->hasTask('db_config'));
+		$this->assertTrue($this->Shell->hasTask('DbConfig'));
+	}
+
+/**
+ * test the hasMethod
+ *
+ * @return void
+ */
+	function testHasMethod() {
+		$this->assertTrue($this->Shell->hasMethod('do_something'));
+		$this->assertFalse($this->Shell->hasMethod('hr'), 'hr is callable');
+		$this->assertFalse($this->Shell->hasMethod('_secret'), '_secret is callable');
+		$this->assertFalse($this->Shell->hasMethod('no_access'), 'no_access is callable');
+	}
+
+/**
+ * test run command calling main.
+ *
+ * @return void
+ */
+	function testRunCommandMain() {
+		$methods = get_class_methods('Shell');
+		$Mock = $this->getMock('Shell', array('main', 'startup'), array(), '', false);
+
+		$Mock->expects($this->once())->method('main')->will($this->returnValue(true));
+		$result = $Mock->runCommand(null, array());
+		$this->assertTrue($result);
+	}
+
+/**
+ * test run command calling a legit method.
+ *
+ * @return void
+ */
+	function testRunCommandWithMethod() {
+		$methods = get_class_methods('Shell');
+		$Mock = $this->getMock('Shell', array('hit_me', 'startup'), array(), '', false);
+
+		$Mock->expects($this->once())->method('hit_me')->will($this->returnValue(true));
+		$result = $Mock->runCommand('hit_me', array());
+		$this->assertTrue($result);
+	}
+
+/**
+ * test run command causing exception on Shell method.
+ *
+ * @return void
+ */
+	function testRunCommandBaseclassMethod() {
+		$Mock = $this->getMock('Shell', array('startup', 'getOptionParser', 'out'), array(), '', false);
+		$Parser = $this->getMock('ConsoleOptionParser', array(), array(), '', false);
+
+		$Parser->expects($this->once())->method('help');
+		$Mock->expects($this->once())->method('getOptionParser')
+			->will($this->returnValue($Parser));
+		$Mock->expects($this->never())->method('hr');
+		$Mock->expects($this->once())->method('out');
+
+		$result = $Mock->runCommand('hr', array());
+	}
+
+/**
+ * test run command causing exception on Shell method.
+ *
+ * @return void
+ */
+	function testRunCommandMissingMethod() {
+		$methods = get_class_methods('Shell');
+		$Mock = $this->getMock('Shell', array('startup', 'getOptionParser', 'out'), array(), '', false);
+		$Parser = $this->getMock('ConsoleOptionParser', array(), array(), '', false);
+
+		$Parser->expects($this->once())->method('help');
+		$Mock->expects($this->never())->method('idontexist');
+		$Mock->expects($this->once())->method('getOptionParser')
+			->will($this->returnValue($Parser));
+		$Mock->expects($this->once())->method('out');
+
+
+		$result = $Mock->runCommand('idontexist', array());
+	}
+
+/**
+ * test that a --help causes help to show.
+ *
+ * @return void
+ */
+	function testRunCommandTriggeringHelp() {
+		$Parser = $this->getMock('ConsoleOptionParser', array(), array(), '', false);
+		$Parser->expects($this->once())->method('parse')
+			->with(array('--help'))
+			->will($this->returnValue(array(array('help' => true), array())));
+		$Parser->expects($this->once())->method('help');
+		
+		$Shell = $this->getMock('Shell', array('getOptionParser', 'out', 'startup', '_welcome'), array(), '', false);
+		$Shell->expects($this->once())->method('getOptionParser')
+			->will($this->returnValue($Parser));
+		$Shell->expects($this->once())->method('out');
+
+		$Shell->runCommand(null, array('--help'));
+	}
+
+/**
+ * test that runCommand will call runCommand on the task.
+ *
+ * @return void
+ */
+	function testRunCommandHittingTask() {
+		$Shell = $this->getMock('Shell', array('hasTask', 'startup'), array(), '', false);
+		$task = $this->getMock('Shell', array('execute', 'runCommand'), array(), '', false);
+		$task->expects($this->any())->method('runCommand')
+			->with('execute', array('one', 'value'));
+
+		$Shell->expects($this->once())->method('startup');
+		$Shell->expects($this->any())->method('hasTask')->will($this->returnValue(true));
+		$Shell->RunCommand = $task;
+
+		$Shell->runCommand('run_command', array('run_command', 'one', 'value'));
+	}
+
+/**
+ * test wrapBlock wrapping text.
+ *
+ * @return void
+ */
+	function testWrapText() {
+		$text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.';
+		$result = $this->Shell->wrapText($text, 33);
+		$expected = <<<TEXT
+This is the song that never ends.
+This is the song that never ends.
+This is the song that never ends.
+TEXT;
+		$this->assertEquals($expected, $result, 'Text not wrapped.');
+
+		$result = $this->Shell->wrapText($text, array('indent' => '  ', 'width' => 33));
+		$expected = <<<TEXT
+  This is the song that never ends.
+  This is the song that never ends.
+  This is the song that never ends.
+TEXT;
+		$this->assertEquals($expected, $result, 'Text not wrapped.');
+	}
 }
diff --git a/cake/tests/cases/console/libs/task_collection.test.php b/cake/tests/cases/console/libs/task_collection.test.php
index e9f9f69c8..da7d6eaef 100644
--- a/cake/tests/cases/console/libs/task_collection.test.php
+++ b/cake/tests/cases/console/libs/task_collection.test.php
@@ -17,15 +17,7 @@
  * @since         CakePHP(tm) v 2.0
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
+
 App::import('Shell', 'TaskCollection', false);
 App::import('Shell', 'Shell', false);
 
@@ -36,9 +28,9 @@ class TaskCollectionTest extends CakeTestCase {
  * @return void
  */
 	function setup() {
+		$shell = $this->getMock('Shell', array(), array(), '', false);
 		$dispatcher = $this->getMock('ShellDispatcher', array(), array(), '', false);
-		$dispatcher->shellPaths = App::path('shells');
-		$this->Tasks = new TaskCollection($dispatcher);
+		$this->Tasks = new TaskCollection($shell, $dispatcher);
 	}
 
 /**
@@ -95,9 +87,11 @@ class TaskCollectionTest extends CakeTestCase {
  */
 	function testLoadPluginTask() {
 		$dispatcher = $this->getMock('ShellDispatcher', array(), array(), '', false);
-		$dispatcher->shellPaths = App::path('shells');
-		$dispatcher->shellPaths[] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'vendors' . DS . 'shells' . DS;
-		$this->Tasks = new TaskCollection($dispatcher);
+		$shell = $this->getMock('Shell', array(), array(), '', false);
+		App::build(array(
+			'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS)
+		));
+		$this->Tasks = new TaskCollection($shell, $dispatcher);
 
 		$result = $this->Tasks->load('TestPlugin.OtherTask');
 		$this->assertType('OtherTaskTask', $result, 'Task class is wrong.');
diff --git a/cake/tests/cases/console/libs/tasks/plugin.test.php b/cake/tests/cases/console/libs/tasks/plugin.test.php
deleted file mode 100644
index 5e75194f1..000000000
--- a/cake/tests/cases/console/libs/tasks/plugin.test.php
+++ /dev/null
@@ -1,249 +0,0 @@
-<?php
-/**
- * PluginTask Test file
- *
- * Test Case for plugin generation shell task
- *
- * PHP 5
- *
- * CakePHP : Rapid Development Framework (http://cakephp.org)
- * Copyright 2006-2010, Cake Software Foundation, Inc.
- *
- * Licensed under The MIT License
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright     Copyright 2006-2010, Cake Software Foundation, Inc.
- * @link          http://cakephp.org CakePHP Project
- * @package       cake
- * @subpackage    cake.tests.cases.console.libs.tasks
- * @since         CakePHP v 1.3.0
- * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
- */
-App::import('Shell', 'Shell', false);
-App::import('Core', array('File'));
-
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'plugin.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
-
-/**
- * PluginTaskPlugin class
- *
- * @package       cake
- * @subpackage    cake.tests.cases.console.libs.tasks
- */
-class PluginTaskTest extends CakeTestCase {
-
-/**
- * setup method
- *
- * @return void
- */
-	public function setUp() {
-		parent::setUp();
-
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
-		$this->Task = $this->getMock('PluginTask', 
-			array('in', 'err', 'createFile', '_stop'),
-			array(&$this->Dispatcher)
-		);
-		$this->Task->path = TMP . 'tests' . DS;
-		
-		$this->_paths = $paths = App::path('plugins');
-		$this->_testPath = array_push($paths, TMP . 'tests' . DS);
-		App::build(array('plugins' => $paths));
-	}
-
-/**
- * teardown
- *
- * @return void
- */
-	public function tearDown() {
-		parent::tearDown();
-		App::build(array('plugins' => $this->_paths));
-	}
-
-/**
- * test bake()
- *
- * @return void
- */
-	public function testBakeFoldersAndFiles() {
-		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue($this->_testPath));
-		$this->Task->expects($this->at(1))->method('in')->will($this->returnValue('y'));
-
-		$path = $this->Task->path . 'bake_test_plugin';
-
-		$file = $path . DS . 'bake_test_plugin_app_controller.php';
-		$this->Task->expects($this->at(3))->method('createFile')
-			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
-
-		$file = $path . DS . 'bake_test_plugin_app_model.php';
-		$this->Task->expects($this->at(4))->method('createFile')
-			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
-
-		$this->Task->bake('BakeTestPlugin');
-
-		$path = $this->Task->path . 'bake_test_plugin';
-		$this->assertTrue(is_dir($path), 'No plugin dir %s');
-
-		$this->assertTrue(is_dir($path . DS . 'config'), 'No config dir %s');
-		$this->assertTrue(is_dir($path . DS . 'config' . DS . 'schema'), 'No schema dir %s');
-		$this->assertTrue(file_exists($path . DS . 'config' . DS . 'schema' . DS . 'empty'), 'No empty file %s');
-
-		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
-		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
-		$this->assertTrue(file_exists($path . DS . 'controllers' . DS . 'components' . DS . 'empty'), 'No empty file %s');
-
-		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
-		$this->assertTrue(file_exists($path . DS . 'models' . DS . 'behaviors' . DS . 'empty'), 'No empty file %s');
-		$this->assertTrue(is_dir($path . DS . 'models' . DS . 'datasources'), 'No datasources dir %s');
-		$this->assertTrue(file_exists($path . DS . 'models' . DS . 'datasources' . DS . 'empty'), 'No empty file %s');
-
-		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
-		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
-		$this->assertTrue(file_exists($path . DS . 'views' . DS . 'helpers' . DS . 'empty'), 'No empty file %s');
-
-		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
-
-		$this->assertTrue(
-			is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'components'), 'No components cases dir %s'
-		);
-		$this->assertTrue(
-			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'components' . DS . 'empty'), 'No empty file %s'
-		);
-
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'behaviors'), 'No behaviors cases dir %s');
-		$this->assertTrue(
-			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'behaviors' . DS . 'empty'), 'No empty file %s'
-		);
-
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'helpers'), 'No helpers cases dir %s');
-		$this->assertTrue(
-			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'helpers' . DS . 'empty'), 'No empty file %s'
-		);
-
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'models'), 'No models cases dir %s');
-		$this->assertTrue(
-			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'models' . DS . 'empty'), 'No empty file %s'
-		);
-
-		$this->assertTrue(
-			is_dir($path . DS . 'tests' . DS . 'cases' . DS . 'controllers'),
-			'No controllers cases dir %s'
-		);
-		$this->assertTrue(
-			file_exists($path . DS . 'tests' . DS . 'cases' . DS . 'controllers' . DS . 'empty'), 'No empty file %s'
-		);
-
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
-		$this->assertTrue(file_exists($path . DS . 'tests' . DS . 'groups' . DS . 'empty'), 'No empty file %s');
-
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
-		$this->assertTrue(file_exists($path . DS . 'tests' . DS . 'fixtures' . DS . 'empty'), 'No empty file %s');
-
-		$this->assertTrue(is_dir($path . DS . 'vendors'), 'No vendors dir %s');
-	
-		$this->assertTrue(is_dir($path . DS . 'vendors' . DS . 'shells'), 'No vendors shells dir %s');
-		$this->assertTrue(is_dir($path . DS . 'vendors' . DS . 'shells' . DS . 'tasks'), 'No vendors shells tasks dir %s');
-		$this->assertTrue(file_exists($path . DS . 'vendors' . DS . 'shells' . DS . 'tasks' . DS . 'empty'), 'No empty file %s');
-		$this->assertTrue(is_dir($path . DS . 'libs'), 'No libs dir %s');
-		$this->assertTrue(is_dir($path . DS . 'webroot'), 'No webroot dir %s');
-
-		$Folder = new Folder($this->Task->path . 'bake_test_plugin');
-		$Folder->delete();
-	}
-
-/**
- * test execute with no args, flowing into interactive,
- *
- * @return void
- */
-	public function testExecuteWithNoArgs() {
-		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('TestPlugin'));
-		$this->Task->expects($this->at(1))->method('in')->will($this->returnValue('3'));
-		$this->Task->expects($this->at(2))->method('in')->will($this->returnValue('y'));
-		$this->Task->expects($this->at(3))->method('in')->will($this->returnValue('n'));
-
-		$path = $this->Task->path . 'test_plugin';
-		$file = $path . DS . 'test_plugin_app_controller.php';
-		$this->Task->expects($this->at(4))->method('createFile')
-			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
-
-		$file = $path . DS . 'test_plugin_app_model.php';
-		$this->Task->expects($this->at(5))->method('createFile')
-			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
-
-		$this->Task->args = array();
-		$this->Task->execute();
-
-		$Folder = new Folder($path);
-		$Folder->delete();
-	}
-
-/**
- * Test Execute
- *
- * @return void
- */
-	public function testExecuteWithOneArg() {
-		$this->Task->expects($this->at(0))->method('in')
-			->will($this->returnValue($this->_testPath));
-		$this->Task->expects($this->at(1))->method('in')
-			->will($this->returnValue('y'));
-
-		$path = $this->Task->path . 'bake_test_plugin';
-		$file = $path . DS . 'bake_test_plugin_app_controller.php';
-		$this->Task->expects($this->at(3))->method('createFile')
-			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
-
-		$path = $this->Task->path . 'bake_test_plugin';
-		$file = $path . DS . 'bake_test_plugin_app_model.php';
-		$this->Task->expects($this->at(4))->method('createFile')
-			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
-		
-		$this->Task->Dispatch->args = array('BakeTestPlugin');
-		$this->Task->args =& $this->Task->Dispatch->args;
-
-		$this->Task->execute();
-
-		$Folder = new Folder($this->Task->path . 'bake_test_plugin');
-		$Folder->delete();
-	}
-
-/**
- * test execute chaining into MVC parts
- *
- * @return void
- */
-	public function testExecuteWithTwoArgs() {
-		$this->Task->Model = $this->getMock('ModelTask', array(), array(&$this->Dispatcher));
-
-		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue($this->_testPath));
-
-		$this->Task->Model->expects($this->once())->method('loadTasks');
-		$this->Task->Model->expects($this->once())->method('execute');
-
-		$Folder = new Folder($this->Task->path . 'bake_test_plugin', true);
-
-		$this->Task->Dispatch->args = array('BakeTestPlugin', 'model');
-		$this->Task->args = $this->Task->Dispatch->args;
-
-		$this->Task->execute();
-		$Folder->delete();
-	}
-}
diff --git a/cake/tests/cases/console/shell_dispatcher.test.php b/cake/tests/cases/console/shell_dispatcher.test.php
new file mode 100644
index 000000000..2da5bbe81
--- /dev/null
+++ b/cake/tests/cases/console/shell_dispatcher.test.php
@@ -0,0 +1,544 @@
+<?php
+/**
+ * ShellDispatcherTest file
+ *
+ * PHP 5
+ *
+ * CakePHP(tm) Tests <http://book.cakephp.org/view/1196/Testing>
+ * Copyright 2005-2010, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc.
+ * @link          http://book.cakephp.org/view/1196/Testing CakePHP(tm) Tests
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ * @since         CakePHP(tm) v 1.2.0.5432
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
+
+/**
+ * TestShellDispatcher class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console
+ */
+class TestShellDispatcher extends ShellDispatcher {
+
+/**
+ * params property
+ *
+ * @var array
+ * @access public
+ */
+	public $params = array();
+
+/**
+ * stopped property
+ *
+ * @var string
+ * @access public
+ */
+	public $stopped = null;
+
+/**
+ * TestShell
+ *
+ * @var mixed
+ * @access public
+ */
+	public $TestShell;
+
+/**
+ * _initEnvironment method
+ *
+ * @return void
+ */
+	protected function _initEnvironment() {
+	}
+
+/**
+ * clear method
+ *
+ * @return void
+ */
+	public function clear() {
+
+	}
+
+/**
+ * _stop method
+ *
+ * @return void
+ */
+	protected function _stop($status = 0) {
+		$this->stopped = 'Stopped with status: ' . $status;
+		return $status;
+	}
+
+/**
+ * getShell
+ *
+ * @param mixed $shell
+ * @return mixed
+ */
+	public function getShell($shell) {
+		return $this->_getShell($shell);
+	}
+
+/**
+ * _getShell
+ *
+ * @param mixed $plugin
+ * @return mixed
+ */
+	protected function _getShell($shell) {
+		if (isset($this->TestShell)) {
+			return $this->TestShell;
+		}
+		return parent::_getShell($shell);
+	}
+}
+
+/**
+ * ShellDispatcherTest
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.libs
+ */
+class ShellDispatcherTest extends CakeTestCase {
+
+/**
+ * setUp method
+ *
+ * @return void
+ */
+	public function setUp() {
+		parent::setUp();
+		App::build(array(
+			'plugins' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS
+			),
+			'shells' => array(
+				CORE_PATH ? CONSOLE_LIBS : ROOT . DS . CONSOLE_LIBS,
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'console' . DS . 'shells' . DS
+			)
+		), true);
+	}
+
+/**
+ * testParseParams method
+ *
+ * @return void
+ */
+	public function testParseParams() {
+		$Dispatcher = new TestShellDispatcher();
+
+		$params = array(
+			'/cake/1.2.x.x/cake/console/cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-working',
+			'/var/www/htdocs'
+		);
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => '/var/www/htdocs/new',
+			'root' => '/var/www/htdocs'
+		);
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array('cake.php');
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-app',
+			'new',
+		);
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'./cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-working',
+			'/cake/1.2.x.x/cake/console'
+		);
+
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
+		);
+
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'./console/cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-working',
+			'/cake/1.2.x.x/cake'
+		);
+		$expected = array(
+			'app' => 'new',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH)
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'./console/cake.php',
+			'bake',
+			'-app',
+			'new',
+			'-dry',
+			'-working',
+			'/cake/1.2.x.x/cake'
+		);
+		$expected = array(
+			'app' => 'new',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'new'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
+			'webroot' => 'webroot'
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEquals($expected, $Dispatcher->params);
+
+		$params = array(
+			'./console/cake.php',
+			'-working',
+			'/cake/1.2.x.x/cake',
+			'schema',
+			'run',
+			'create',
+			'-dry',
+			'-f',
+			'-name',
+			'DbAcl'
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH . DS . 'app'),
+			'root' => str_replace('\\', '/', CAKE_CORE_INCLUDE_PATH),
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$expected = array(
+			'./console/cake.php', 'schema', 'run', 'create', '-dry', '-f', '-name', 'DbAcl'
+		);
+		$this->assertEqual($expected, $Dispatcher->args);
+
+		$params = array(
+			'/cake/1.2.x.x/cake/console/cake.php',
+			'-working',
+			'/cake/1.2.x.x/app',
+			'schema',
+			'run',
+			'create',
+			'-dry',
+			'-name',
+			'DbAcl'
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => '/cake/1.2.x.x/app',
+			'root' => '/cake/1.2.x.x',
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-working',
+			'C:/wamp/www/cake/app',
+			'bake',
+			'-app',
+			'C:/wamp/www/apps/cake/app',
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => 'C:\wamp\www\apps\cake\app',
+			'root' => 'C:\wamp\www\apps\cake'
+		);
+
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-working',
+			'C:\wamp\www\cake\app',
+			'bake',
+			'-app',
+			'C:\wamp\www\apps\cake\app',
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => 'C:\wamp\www\apps\cake\app',
+			'root' => 'C:\wamp\www\apps\cake'
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-working',
+			'C:\wamp\www\apps',
+			'bake',
+			'-app',
+			'cake\app',
+			'-url',
+			'http://example.com/some/url/with/a/path'
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => 'C:\wamp\www\apps\cake\app',
+			'root' => 'C:\wamp\www\apps\cake',
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'/home/amelo/dev/cake-common/cake/console/cake.php',
+			'-root',
+			'/home/amelo/dev/lsbu-vacancy',
+			'-working',
+			'/home/amelo/dev/lsbu-vacancy',
+			'-app',
+			'app',
+		);
+		$expected = array(
+			'app' => 'app',
+			'webroot' => 'webroot',
+			'working' => '/home/amelo/dev/lsbu-vacancy/app',
+			'root' => '/home/amelo/dev/lsbu-vacancy',
+		);
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+
+		$params = array(
+			'cake.php',
+			'-working',
+			'D:\www',
+			'bake',
+			'my_app',
+		);
+		$expected = array(
+			'working' => 'D:\www',
+			'app' => 'www',
+			'root' => 'D:',
+			'webroot' => 'webroot'
+		);
+
+		$Dispatcher->params = $Dispatcher->args = array();
+		$Dispatcher->parseParams($params);
+		$this->assertEqual($expected, $Dispatcher->params);
+	}
+
+/**
+ * Verify loading of (plugin-) shells
+ *
+ * @return void
+ */
+	public function testGetShell() {
+		$this->skipIf(class_exists('SampleShell'), '%s SampleShell Class already loaded');
+		$this->skipIf(class_exists('ExampleShell'), '%s ExampleShell Class already loaded');
+
+		$Dispatcher = new TestShellDispatcher();
+
+		$result = $Dispatcher->getShell('sample');
+		$this->assertInstanceOf('SampleShell', $result);
+
+		$Dispatcher = new TestShellDispatcher();
+		$result = $Dispatcher->getShell('test_plugin.example');
+		$this->assertInstanceOf('ExampleShell', $result);
+	}
+
+/**
+ * Verify correct dispatch of Shell subclasses with a main method
+ *
+ * @return void
+ */
+	public function testDispatchShellWithMain() {
+		$Dispatcher = new TestShellDispatcher();
+		$Mock = $this->getMock('Shell', array(), array(&$Dispatcher), 'MockWithMainShell');
+
+		$Mock->expects($this->once())->method('initialize');
+		$Mock->expects($this->once())->method('loadTasks');
+		$Mock->expects($this->once())->method('runCommand')
+			->with(null, array())
+			->will($this->returnValue(true));
+
+		$Dispatcher->TestShell = $Mock;
+
+		$Dispatcher->args = array('mock_with_main');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+	}
+
+/**
+ * Verify correct dispatch of Shell subclasses without a main method
+ *
+ * @return void
+ */
+	public function testDispatchShellWithoutMain() {
+		$Dispatcher = new TestShellDispatcher();
+		$Shell = $this->getMock('Shell', array(), array(&$Dispatcher), 'MockWithoutMainShell');
+
+		$Shell = new MockWithoutMainShell($Dispatcher);
+		$this->mockObjects[] = $Shell;
+
+		$Shell->expects($this->once())->method('initialize');
+		$Shell->expects($this->once())->method('loadTasks');
+		$Shell->expects($this->once())->method('runCommand')
+			->with('initdb', array('initdb'))
+			->will($this->returnValue(true));
+
+		$Dispatcher->TestShell = $Shell;
+
+		$Dispatcher->args = array('mock_without_main', 'initdb');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+	}
+
+/**
+ * Verify correct dispatch of custom classes with a main method
+ *
+ * @return void
+ */
+	public function testDispatchNotAShellWithMain() {
+		$Dispatcher = new TestShellDispatcher();
+		$methods = get_class_methods('Object');
+		array_push($methods, 'main', 'initdb', 'initialize', 'loadTasks', 'startup', '_secret');
+		$Shell = $this->getMock('Object', $methods, array(), 'MockWithMainNotAShell');
+
+		$Shell->expects($this->never())->method('initialize');
+		$Shell->expects($this->never())->method('loadTasks');
+		$Shell->expects($this->once())->method('startup');
+		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
+		$Dispatcher->TestShell = $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithMainNotAShell($Dispatcher);
+		$this->mockObjects[] = $Shell;
+		$Shell->expects($this->once())->method('initdb')->will($this->returnValue(true));
+		$Shell->expects($this->once())->method('startup');
+		$Dispatcher->TestShell = $Shell;
+
+		$Dispatcher->args = array('mock_with_main_not_a', 'initdb');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+	}
+
+/**
+ * Verify correct dispatch of custom classes without a main method
+ *
+ * @return void
+ */
+	public function testDispatchNotAShellWithoutMain() {
+		$Dispatcher = new TestShellDispatcher();
+		$methods = get_class_methods('Object');
+		array_push($methods, 'main', 'initdb', 'initialize', 'loadTasks', 'startup', '_secret');
+		$Shell = $this->getMock('Object', $methods, array(&$Dispatcher), 'MockWithoutMainNotAShell');
+
+		$Shell->expects($this->never())->method('initialize');
+		$Shell->expects($this->never())->method('loadTasks');
+		$Shell->expects($this->once())->method('startup');
+		$Shell->expects($this->once())->method('main')->will($this->returnValue(true));
+		$Dispatcher->TestShell = $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+		$this->assertEqual($Dispatcher->args, array());
+
+		$Shell = new MockWithoutMainNotAShell($Dispatcher);
+		$this->mockObjects[] = $Shell;
+		$Shell->expects($this->once())->method('initdb')->will($this->returnValue(true));
+		$Shell->expects($this->once())->method('startup');
+		$Dispatcher->TestShell = $Shell;
+
+		$Dispatcher->args = array('mock_without_main_not_a', 'initdb');
+		$result = $Dispatcher->dispatch();
+		$this->assertTrue($result);
+	}
+
+/**
+ * Verify shifting of arguments
+ *
+ * @return void
+ */
+	public function testShiftArgs() {
+		$Dispatcher = new TestShellDispatcher();
+
+		$Dispatcher->args = array('a', 'b', 'c');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
+		$this->assertIdentical($Dispatcher->args, array('b', 'c'));
+
+		$Dispatcher->args = array('a' => 'b', 'c', 'd');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'b');
+		$this->assertIdentical($Dispatcher->args, array('c', 'd'));
+
+		$Dispatcher->args = array('a', 'b' => 'c', 'd');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
+		$this->assertIdentical($Dispatcher->args, array('b' => 'c', 'd'));
+
+		$Dispatcher->args = array(0 => 'a',  2 => 'b', 30 => 'c');
+		$this->assertEqual($Dispatcher->shiftArgs(), 'a');
+		$this->assertIdentical($Dispatcher->args, array(0 => 'b', 1 => 'c'));
+
+		$Dispatcher->args = array();
+		$this->assertNull($Dispatcher->shiftArgs());
+		$this->assertIdentical($Dispatcher->args, array());
+	}
+
+}
diff --git a/cake/tests/cases/console/libs/acl.test.php b/cake/tests/cases/console/shells/acl.test.php
similarity index 82%
rename from cake/tests/cases/console/libs/acl.test.php
rename to cake/tests/cases/console/shells/acl.test.php
index 5df62fdde..305ee1ebc 100644
--- a/cake/tests/cases/console/libs/acl.test.php
+++ b/cake/tests/cases/console/shells/acl.test.php
@@ -18,21 +18,9 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'Acl');
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-if (!class_exists('AclShell')) {
-	require CAKE . 'console' .  DS . 'libs' . DS . 'acl.php';
-}
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * AclShellTest class
@@ -61,14 +49,13 @@ class AclShellTest extends CakeTestCase {
 		Configure::write('Acl.database', 'test');
 		Configure::write('Acl.classname', 'DbAcl');
 
-		$this->Dispatcher = $this->getMock(
-			'ShellDispatcher', 
-			array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'dispatch', 'clear')
-		);
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock(
 			'AclShell',
-			array('in', 'out', 'hr', 'createFile', 'error', 'err'),
-			array(&$this->Dispatcher)
+			array('in', 'out', 'hr', 'createFile', 'error', 'err', 'clear', 'dispatchShell'),
+			array($out, $out, $in)
 		);
 		$collection = new ComponentCollection();
 		$this->Task->Acl = new AclComponent($collection);
@@ -145,9 +132,9 @@ class AclShellTest extends CakeTestCase {
  */
 	public function testCreate() {
 		$this->Task->args = array('aro', 'root', 'User.1');
-		$this->Task->expects($this->at(0))->method('out')->with("New Aro 'User.1' created.\n", true);
-		$this->Task->expects($this->at(1))->method('out')->with("New Aro 'User.3' created.\n", true);
-		$this->Task->expects($this->at(2))->method('out')->with("New Aro 'somealias' created.\n", true);
+		$this->Task->expects($this->at(0))->method('out')->with("<success>New Aro</success> 'User.1' created.", 2);
+		$this->Task->expects($this->at(1))->method('out')->with("<success>New Aro</success> 'User.3' created.", 2);
+		$this->Task->expects($this->at(2))->method('out')->with("<success>New Aro</success> 'somealias' created.", 2);
 
 		$this->Task->create();
 
@@ -187,7 +174,7 @@ class AclShellTest extends CakeTestCase {
 	public function testDelete() {
 		$this->Task->args = array('aro', 'AuthUser.1');
 		$this->Task->expects($this->at(0))->method('out')
-			->with("Aro deleted.\n", true);
+			->with("<success>Aro deleted.</success>", 2);
 		$this->Task->delete();
 
 		$Aro = ClassRegistry::init('Aro');
@@ -217,7 +204,7 @@ class AclShellTest extends CakeTestCase {
 	public function testGrant() {
 		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create');
 		$this->Task->expects($this->at(0))->method('out')
-			->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission granted/'), true);
+			->with($this->matchesRegularExpression('/granted/'), true);
 		$this->Task->grant();
 
 		$node = $this->Task->Acl->Aro->read(null, 4);
@@ -249,13 +236,13 @@ class AclShellTest extends CakeTestCase {
  */
 	public function testCheck() {
 		$this->Task->expects($this->at(0))->method('out')
-			->with(new PHPUnit_Framework_Constraint_PCREMatch('/not allowed/'), true);
+			->with($this->matchesRegularExpression('/not allowed/'), true);
 		$this->Task->expects($this->at(1))->method('out')
-			->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission granted/'), true);
+			->with($this->matchesRegularExpression('/granted/'), true);
 		$this->Task->expects($this->at(2))->method('out')
-			->with(new PHPUnit_Framework_Constraint_PCREMatch('/is allowed/'), true);
+			->with($this->matchesRegularExpression('/is.*allowed/'), true);
 		$this->Task->expects($this->at(3))->method('out')
-			->with(new PHPUnit_Framework_Constraint_PCREMatch('/not allowed/'), true);
+			->with($this->matchesRegularExpression('/not.*allowed/'), true);
 
 		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', '*');
 		$this->Task->check();
@@ -277,9 +264,9 @@ class AclShellTest extends CakeTestCase {
  */
 	public function testInherit() {
 		$this->Task->expects($this->at(0))->method('out')
-			->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission granted/'), true);
+			->with($this->matchesRegularExpression('/Permission .*granted/'), true);
 		$this->Task->expects($this->at(1))->method('out')
-			->with(new PHPUnit_Framework_Constraint_PCREMatch('/Permission inherited/'), true);
+			->with($this->matchesRegularExpression('/Permission .*inherited/'), true);
 		
 		$this->Task->args = array('AuthUser.2', 'ROOT/Controller1', 'create');
 		$this->Task->grant();
@@ -311,9 +298,9 @@ class AclShellTest extends CakeTestCase {
  * @return void
  */
 	function testInitDb() {
-		$this->Task->Dispatch->expects($this->once())->method('dispatch');
+		$this->Task->expects($this->once())->method('dispatchShell')
+			->with('schema create DbAcl');
+	
 		$this->Task->initdb();
-
-		$this->assertEqual($this->Task->Dispatch->args, array('schema', 'create', 'DbAcl'));
 	}
 }
diff --git a/cake/tests/cases/console/libs/api.test.php b/cake/tests/cases/console/shells/api.test.php
similarity index 82%
rename from cake/tests/cases/console/libs/api.test.php
rename to cake/tests/cases/console/shells/api.test.php
index fda9b1ba7..109cf57f7 100644
--- a/cake/tests/cases/console/libs/api.test.php
+++ b/cake/tests/cases/console/shells/api.test.php
@@ -18,21 +18,10 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'Api');
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-if (!class_exists('ApiShell')) {
-	require CAKE . 'console' .  DS . 'libs' . DS . 'api.php';
-}
 
 /**
  * ApiShellTest class
@@ -49,14 +38,13 @@ class ApiShellTest extends CakeTestCase {
  */
 	public function setUp() {
 		parent::setUp();
-		$this->Dispatcher = $this->getMock(
-			'ShellDispatcher', 
-			array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'dispatch', 'clear')
-		);
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Shell = $this->getMock(
 			'ApiShell',
 			array('in', 'out', 'createFile', 'hr', '_stop'),
-			array(&$this->Dispatcher)
+			array(	$out, $out, $in)
 		);
 	}
 
diff --git a/cake/tests/cases/console/libs/bake.test.php b/cake/tests/cases/console/shells/bake.test.php
similarity index 79%
rename from cake/tests/cases/console/libs/bake.test.php
rename to cake/tests/cases/console/shells/bake.test.php
index 10cec6576..fc5e79163 100644
--- a/cake/tests/cases/console/libs/bake.test.php
+++ b/cake/tests/cases/console/shells/bake.test.php
@@ -19,23 +19,13 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'Bake', false);
+App::import('Shell', 'tasks/model', false);
+App::import('Shell', 'tasks/controller', false);
+App::import('Shell', 'tasks/db_config', false);
+
 App::import('Core', 'Controller');
-
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'bake.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'controller.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'db_config.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 if (!class_exists('UsersController')) {
 	class UsersController extends Controller {
@@ -60,16 +50,14 @@ class BakeShellTest extends CakeTestCase {
  */
 	public function setUp() {
 		parent::setUp();
-		$this->Dispatcher = $this->getMock(
-			'ShellDispatcher', 
-			array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear')
-		);
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Shell = $this->getMock(
 			'BakeShell',
 			array('in', 'out', 'hr', 'err', 'createFile', '_stop', '_checkUnitTest'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
-		$this->Shell->Dispatch->shellPaths = App::path('shells');
 	}
 
 /**
diff --git a/cake/tests/cases/console/shells/command_list.test.php b/cake/tests/cases/console/shells/command_list.test.php
new file mode 100644
index 000000000..bdabc2230
--- /dev/null
+++ b/cake/tests/cases/console/shells/command_list.test.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * CommandList file
+ *
+ * PHP 5
+ *
+ * CakePHP :  Rapid Development Framework (http://cakephp.org)
+ * Copyright 2006-2010, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2006-2010, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs
+ * @since         CakePHP v 2.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'CommandList', false);
+
+
+class TestStringOutput extends ConsoleOutput {
+	public $output = '';
+
+	protected function _write($message) {
+		$this->output .= $message;
+	}
+}
+
+class CommandListTest extends CakeTestCase {
+/**
+ * setUp method
+ *
+ * @return void
+ */
+	public function setUp() {
+		parent::setUp();
+		App::build(array(
+			'plugins' => array(
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS
+			),
+			'shells' => array(
+				CORE_PATH ? 
+					CORE_PATH . CAKE . 'console' . DS . 'shells' . DS : 
+					CAKE_CORE_INCLUDE_PATH . DS . 'cake' . DS . 'console' . DS . 'shells' .DS, 
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'console' . DS . 'shells' . DS
+			)
+		), true);
+		App::objects('plugin', null, false);
+
+		$out = new TestStringOutput();
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
+		$this->Shell = $this->getMock(
+			'CommandListShell',
+			array('in', '_stop', 'clear'),
+			array($out, $out, $in)
+		);
+	}
+
+/**
+ * teardown
+ *
+ * @return void
+ */
+	function tearDown() {
+		parent::tearDown();
+		unset($this->Shell);
+	}
+
+/**
+ * test that main finds core shells.
+ *
+ * @return void
+ */
+	function testMain() {
+		$this->Shell->main();
+		$output = $this->Shell->stdout->output;
+
+		$expected = "/example \[.*TestPlugin, TestPluginTwo.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/welcome \[.*TestPluginTwo.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/acl \[.*CORE.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/api \[.*CORE.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/bake \[.*CORE.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/console \[.*CORE.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/i18n \[.*CORE.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/schema \[.*CORE.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/testsuite \[.*CORE.*\]/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/sample \[.*app.*\]/";
+		$this->assertPattern($expected, $output);
+	}
+
+/**
+ * Test the sort param
+ *
+ * @return void
+ */
+	function testSortPlugin() {
+		$this->Shell->params['sort'] = true;
+		$this->Shell->main();
+
+		$output = $this->Shell->stdout->output;
+
+		$expected = "/\[.*App.*\]\n[ ]+sample/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/\[.*TestPluginTwo.*\]\n[ ]+example, welcome/";
+		$this->assertPattern($expected, $output);
+
+		$expected = "/\[.*TestPlugin.*\]\n[ ]+example/";
+		$this->assertPattern($expected, $output);
+		
+		$expected = "/\[.*Core.*\]\n[ ]+acl, api, bake, command_list, console, i18n, schema, testsuite/";
+		$this->assertPattern($expected, $output);
+	}
+
+/**
+ * test xml output.
+ *
+ * @return void
+ */
+	function testMainXml() {
+		$this->Shell->params['xml'] = true;
+		$this->Shell->main();
+
+		$output = $this->Shell->stdout->output;
+
+		$find = '<shell name="sample" call_as="sample" provider="app" help="sample -h"/>';
+		$this->assertContains($find, $output);
+
+		$find = '<shell name="bake" call_as="bake" provider="CORE" help="bake -h"/>';
+		$this->assertContains($find, $output);
+		
+		$find = '<shell name="welcome" call_as="test_plugin_two.welcome" provider="TestPluginTwo" help="test_plugin_two.welcome -h"/>';
+		$this->assertContains($find, $output);
+	}
+}
diff --git a/cake/tests/cases/console/libs/schema.test.php b/cake/tests/cases/console/shells/schema.test.php
similarity index 94%
rename from cake/tests/cases/console/libs/schema.test.php
rename to cake/tests/cases/console/shells/schema.test.php
index 33c43260e..5fd76e63d 100644
--- a/cake/tests/cases/console/libs/schema.test.php
+++ b/cake/tests/cases/console/shells/schema.test.php
@@ -18,22 +18,12 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'Schema', false);
 App::import('Model', 'CakeSchema', false);
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
 
-if (!class_exists('SchemaShell')) {
-	require CAKE . 'console' .  DS . 'libs' . DS . 'schema.php';
-}
 
 /**
  * Test for Schema database management
@@ -119,15 +109,15 @@ class SchemaShellTest extends CakeTestCase {
  *
  * @return void
  */
-	public function setup() {
-		$this->Dispatcher = $this->getMock(
-			'ShellDispatcher', 
-			array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear')
-		);
+	public function setUp() {
+		parent::setUp();
+
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
 		$this->Shell = $this->getMock(
 			'SchemaShell',
 			array('in', 'out', 'hr', 'createFile', 'error', 'err', '_stop'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
 	}
 
@@ -136,13 +126,12 @@ class SchemaShellTest extends CakeTestCase {
  *
  * @return void
  */
-	public function teardown() {
-		ClassRegistry::flush();
+	public function tearDown() {
+		parent::tearDown();
 		if (!empty($this->file) && $this->file instanceof File) {
 			$this->file->delete();
 			unset($this->file);
 		}
-		App::build();
 	}
 
 /**
@@ -157,7 +146,7 @@ class SchemaShellTest extends CakeTestCase {
 		$this->assertEqual(strtolower($this->Shell->Schema->name), strtolower(APP_DIR));
 		$this->assertEqual($this->Shell->Schema->file, 'schema.php');
 
-		unset($this->Shell->Schema);
+		$this->Shell->Schema = null;
 		$this->Shell->params = array(
 			'name' => 'TestSchema'
 		);
@@ -167,7 +156,7 @@ class SchemaShellTest extends CakeTestCase {
 		$this->assertEqual($this->Shell->Schema->connection, 'default');
 		$this->assertEqual($this->Shell->Schema->path, APP . 'config' . DS . 'schema');
 
-		unset($this->Shell->Schema);
+		$this->Shell->Schema = null;
 		$this->Shell->params = array(
 			'file' => 'other_file.php',
 			'connection' => 'test',
@@ -264,7 +253,7 @@ class SchemaShellTest extends CakeTestCase {
 		$this->file =& new File(TMP . 'tests' . DS . 'dump_test.sql');
 		$contents = $this->file->read();
 
-		$this->assertPattern('/CREATE TABLE `acos`/', $contents);
+		$this->assertPattern('/CREATE TABLE `test_plugin_acos`/', $contents);
 		$this->assertPattern('/id/', $contents);
 		$this->assertPattern('/model/', $contents);
 
@@ -419,6 +408,9 @@ class SchemaShellTest extends CakeTestCase {
 		$this->assertTrue(in_array($db->config['prefix'] . 'acos', $sources), 'acos should be present.');
 		$this->assertFalse(in_array($db->config['prefix'] . 'aros', $sources), 'aros should not be found.');
 		$this->assertFalse(in_array('aros_acos', $sources), 'aros_acos should not be found.');
+
+		$schema = new DbAclSchema();
+		$db->execute($db->dropSchema($schema, 'acos'));
 	}
 
 /**
@@ -435,7 +427,7 @@ class SchemaShellTest extends CakeTestCase {
 
 		$this->Shell->params = array(
 			'connection' => 'test',
-			'f' => true
+			'force' => true
 		);
 		$this->Shell->args = array('SchemaShellTest', 'articles');
 		$this->Shell->startup();
@@ -483,6 +475,9 @@ class SchemaShellTest extends CakeTestCase {
 
 		$db =& ConnectionManager::getDataSource('test');
 		$sources = $db->listSources();
-		$this->assertTrue(in_array($db->config['prefix'] . 'acos', $sources));
+		$this->assertTrue(in_array($db->config['prefix'] . 'test_plugin_acos', $sources));
+
+		$schema = new TestPluginAppSchema();
+		$db->execute($db->dropSchema($schema, 'test_plugin_acos'));
 	}
 }
diff --git a/cake/tests/cases/console/libs/tasks/controller.test.php b/cake/tests/cases/console/shells/tasks/controller.test.php
similarity index 94%
rename from cake/tests/cases/console/libs/tasks/controller.test.php
rename to cake/tests/cases/console/shells/tasks/controller.test.php
index 67384215f..ba8ef1fef 100644
--- a/cake/tests/cases/console/libs/tasks/controller.test.php
+++ b/cake/tests/cases/console/shells/tasks/controller.test.php
@@ -20,24 +20,15 @@
 App::import('Core', 'ClassRegistry');
 App::import('View', 'Helper', false);
 App::import('Shell', 'Shell', false);
+App::import('Shell', array(
+	'tasks/project',
+	'tasks/controller',
+	'tasks/model',
+	'tasks/template',
+	'tasks/test'
+));
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'project.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'controller.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'test.php';
-
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 $imported = App::import('Model', 'BakeArticle');
 $imported = $imported || App::import('Model', 'BakeComment');
@@ -77,27 +68,25 @@ class ControllerTaskTest extends CakeTestCase {
  * @return void
  */
 	public function setUp() {
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
 		$this->Task = $this->getMock('ControllerTask', 
 			array('in', 'out', 'err', 'hr', 'createFile', '_stop', '_checkUnitTest'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
-		$this->Task->name = 'ControllerTask';
-		$this->Task->Dispatch->shellPaths = App::path('shells');
-		$this->Task->Template =& new TemplateTask($this->Task->Dispatch);
+		$this->Task->name = 'Controller';
+		$this->Task->Template = new TemplateTask($out, $out, $in);
 		$this->Task->Template->params['theme'] = 'default';
 
 		$this->Task->Model = $this->getMock('ModelTask', 
 			array('in', 'out', 'err', 'createFile', '_stop', '_checkUnitTest'), 
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
 		$this->Task->Project = $this->getMock('ProjectTask', 
 			array('in', 'out', 'err', 'createFile', '_stop', '_checkUnitTest', 'getPrefix'), 
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
-		$this->Task->Test = $this->getMock('TestTask', array(), array(&$this->Dispatcher));
+		$this->Task->Test = $this->getMock('TestTask', array(), array($out, $out, $in));
 	}
 
 /**
@@ -106,7 +95,7 @@ class ControllerTaskTest extends CakeTestCase {
  * @return void
  */
 	public function teardown() {
-		unset($this->Task, $this->Dispatcher);
+		unset($this->Task);
 		ClassRegistry::flush();
 	}
 
@@ -600,7 +589,8 @@ class ControllerTaskTest extends CakeTestCase {
 		}
 		$this->Task->connection = 'test';
 		$this->Task->path = '/my/path/';
-		$this->Task->args = array('BakeArticles', 'public');
+		$this->Task->args = array('BakeArticles');
+		$this->Task->params = array('public' => true);
 
 		$filename = '/my/path/bake_articles_controller.php';
 		$expected = new PHPUnit_Framework_Constraint_Not(new PHPUnit_Framework_Constraint_PCREMatch('/\$scaffold/'));
@@ -622,7 +612,8 @@ class ControllerTaskTest extends CakeTestCase {
 		$this->Task->Project->expects($this->any())->method('getPrefix')->will($this->returnValue('admin_'));
 		$this->Task->connection = 'test';
 		$this->Task->path = '/my/path/';
-		$this->Task->args = array('BakeArticles', 'public', 'admin');
+		$this->Task->args = array('BakeArticles');
+		$this->Task->params = array('public' => true, 'admin' => true);
 
 		$filename = '/my/path/bake_articles_controller.php';
 		$this->Task->expects($this->once())->method('createFile')->with(
@@ -643,7 +634,8 @@ class ControllerTaskTest extends CakeTestCase {
 		$this->Task->Project->expects($this->any())->method('getPrefix')->will($this->returnValue('admin_'));
 		$this->Task->connection = 'test';
 		$this->Task->path = '/my/path/';
-		$this->Task->args = array('BakeArticles', 'admin');
+		$this->Task->args = array('BakeArticles');
+		$this->Task->params = array('admin' => true);
 
 		$filename = '/my/path/bake_articles_controller.php';
 		$this->Task->expects($this->once())->method('createFile')->with(
diff --git a/cake/tests/cases/console/libs/tasks/db_config.test.php b/cake/tests/cases/console/shells/tasks/db_config.test.php
similarity index 76%
rename from cake/tests/cases/console/libs/tasks/db_config.test.php
rename to cake/tests/cases/console/shells/tasks/db_config.test.php
index 358d4d125..aca87ac94 100644
--- a/cake/tests/cases/console/libs/tasks/db_config.test.php
+++ b/cake/tests/cases/console/shells/tasks/db_config.test.php
@@ -18,19 +18,9 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'tasks/DbConfig');
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'db_config.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 
 class TEST_DATABASE_CONFIG {
@@ -70,16 +60,15 @@ class DbConfigTaskTest extends CakeTestCase {
  */
 	public function setUp() {
 		parent::setUp();
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock('DbConfigTask', 
 			array('in', 'out', 'err', 'hr', 'createFile', '_stop', '_checkUnitTest', '_verify'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
-		$this->Task->Dispatch->shellPaths = App::path('shells');
 
-		$this->Task->params['working'] = rtrim(APP, DS);
+		$this->Task->path = APP . 'config' . DS;
 		$this->Task->databaseClassName = 'TEST_DATABASE_CONFIG';
 	}
 
@@ -90,7 +79,7 @@ class DbConfigTaskTest extends CakeTestCase {
  */
 	public function tearDown() {
 		parent::tearDown();
-		unset($this->Task, $this->Dispatcher);
+		unset($this->Task);
 	}
 
 /**
@@ -101,7 +90,7 @@ class DbConfigTaskTest extends CakeTestCase {
 	public function testGetConfig() {
 		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('otherOne'));
 		$result = $this->Task->getConfig();
-		$this->assertEqual($result, 'otherOne');
+		$this->assertEquals('otherOne', $result);
 	}
 
 /**
@@ -110,11 +99,9 @@ class DbConfigTaskTest extends CakeTestCase {
  * @return void
  */
 	public function testInitialize() {
-		$this->assertTrue(empty($this->Task->path));
 		$this->Task->initialize();
 		$this->assertFalse(empty($this->Task->path));
-		$this->assertEqual($this->Task->path, APP . 'config' . DS);
-
+		$this->assertEquals(APP . 'config' . DS, $this->Task->path);
 	}
 
 /**
@@ -124,7 +111,13 @@ class DbConfigTaskTest extends CakeTestCase {
  */
 	public function testExecuteIntoInteractive() {
 		$this->Task->initialize();
-		$this->Task = $this->getMock('DbConfigTask', array('in', '_stop', 'createFile'), array(&$this->Dispatcher));
+
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+		$this->Task = $this->getMock(
+			'DbConfigTask',
+			array('in', '_stop', 'createFile', 'bake'), array($out, $out, $in)
+		);
 
 		$this->Task->expects($this->once())->method('_stop');
 		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('default')); //name
@@ -139,6 +132,22 @@ class DbConfigTaskTest extends CakeTestCase {
 		$this->Task->expects($this->at(12))->method('in')->will($this->returnValue('n')); //encoding
 		$this->Task->expects($this->at(13))->method('in')->will($this->returnValue('y')); //looks good
 		$this->Task->expects($this->at(14))->method('in')->will($this->returnValue('n')); //another
+		$this->Task->expects($this->at(15))->method('bake')
+			->with(array(
+				array(
+					'name' => 'default',
+					'driver' => 'mysql',
+					'persistent' => 'false',
+					'host' => 'localhost',
+					'login' => 'root',
+					'password' => 'password',
+					'database' => 'cake_test',
+					'prefix' => null,
+					'encoding' => null,
+					'port' => '',
+					'schema' => null
+				)
+			));
 
 		$result = $this->Task->execute();
 	}
diff --git a/cake/tests/cases/console/libs/tasks/extract.test.php b/cake/tests/cases/console/shells/tasks/extract.test.php
similarity index 71%
rename from cake/tests/cases/console/libs/tasks/extract.test.php
rename to cake/tests/cases/console/shells/tasks/extract.test.php
index 44898b72b..21993e0d4 100644
--- a/cake/tests/cases/console/libs/tasks/extract.test.php
+++ b/cake/tests/cases/console/shells/tasks/extract.test.php
@@ -21,19 +21,9 @@
  */
 App::import('Core', 'Folder');
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'tasks/Extract', false);
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'extract.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * ExtractTaskTest class
@@ -49,10 +39,16 @@ class ExtractTaskTest extends CakeTestCase {
  * @return void
  */
 	public function setUp() {
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
-		$this->Task =& new ExtractTask($this->Dispatcher);
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
+		$this->Task = $this->getMock(
+			'ExtractTask',
+			array('in', 'out', 'err', '_stop'),
+			array($out, $out, $in)
+		);
+		$this->path = TMP . 'tests' . DS . 'extract_task_test';
+		$Folder = new Folder($this->path . DS . 'locale', true);
 	}
 
 /**
@@ -61,7 +57,11 @@ class ExtractTaskTest extends CakeTestCase {
  * @return void
  */
 	public function tearDown() {
-		ClassRegistry::flush();
+		parent::tearDown();
+		unset($this->Task);
+
+		$Folder = new Folder($this->path);
+		$Folder->delete();
 	}
 
 /**
@@ -70,19 +70,18 @@ class ExtractTaskTest extends CakeTestCase {
  * @return void
  */
 	public function testExecute() {
-		$path = TMP . 'tests' . DS . 'extract_task_test';
-		new Folder($path . DS . 'locale', true);
-
 		$this->Task->interactive = false;
 
 		$this->Task->params['paths'] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'pages';
-		$this->Task->params['output'] = $path . DS;
-		$this->Dispatcher->expects($this->never())->method('stderr');
-		$this->Dispatcher->expects($this->never())->method('_stop');
+		$this->Task->params['output'] = $this->path . DS;
+		$this->Task->expects($this->never())->method('err');
+		$this->Task->expects($this->any())->method('in')
+			->will($this->returnValue('y'));
+		$this->Task->expects($this->never())->method('_stop');
 
 		$this->Task->execute();
-		$this->assertTrue(file_exists($path . DS . 'default.pot'));
-		$result = file_get_contents($path . DS . 'default.pot');
+		$this->assertTrue(file_exists($this->path . DS . 'default.pot'));
+		$result = file_get_contents($this->path . DS . 'default.pot');
 
 		$pattern = '/"Content-Type\: text\/plain; charset\=utf-8/';
 		$this->assertPattern($pattern, $result);
@@ -134,7 +133,7 @@ class ExtractTaskTest extends CakeTestCase {
 		$this->assertPattern($pattern, $result);
 
 		// extract.ctp - reading the domain.pot
-		$result = file_get_contents($path . DS . 'domain.pot');
+		$result = file_get_contents($this->path . DS . 'domain.pot');
 
 		$pattern = '/msgid "You have %d new message."\nmsgid_plural "You have %d new messages."/';
 		$this->assertNoPattern($pattern, $result);
@@ -145,9 +144,32 @@ class ExtractTaskTest extends CakeTestCase {
 		$this->assertPattern($pattern, $result);
 		$pattern = '/msgid "You deleted %d message \(domain\)."\nmsgid_plural "You deleted %d messages \(domain\)."/';
 		$this->assertPattern($pattern, $result);
+	}
 
-		$Folder = new Folder($path);
-		$Folder->delete();
+/**
+ * test exclusions
+ *
+ * @return void
+ */
+	function testExtractWithExclude() {
+		$this->Task->interactive = false;
+
+		$this->Task->params['paths'] = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views';
+		$this->Task->params['output'] = $this->path . DS;
+		$this->Task->params['exclude'] = 'pages,layouts';
+
+		$this->Task->expects($this->any())->method('in')
+			->will($this->returnValue('y'));
+
+		$this->Task->execute();
+		$this->assertTrue(file_exists($this->path . DS . 'default.pot'));
+		$result = file_get_contents($this->path . DS . 'default.pot');
+
+		$pattern = '/\#: .*extract\.ctp:6\n/';
+		$this->assertNotRegExp($pattern, $result);
+		
+		$pattern = '/\#: .*default\.ctp:26\n/';
+		$this->assertNotRegExp($pattern, $result);
 	}
 
 /**
@@ -156,21 +178,18 @@ class ExtractTaskTest extends CakeTestCase {
  * @return void
  */
 	function testExtractMultiplePaths() {
-		$path = TMP . 'tests' . DS . 'extract_task_test';
-		new Folder($path . DS . 'locale', true);
-
 		$this->Task->interactive = false;
 
 		$this->Task->params['paths'] = 
 			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'pages,' .
 			TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'posts';
 	
-		$this->Task->params['output'] = $path . DS;
-		$this->Task->Dispatch->expects($this->never())->method('stderr');
-		$this->Task->Dispatch->expects($this->never())->method('_stop');
+		$this->Task->params['output'] = $this->path . DS;
+		$this->Task->expects($this->never())->method('err');
+		$this->Task->expects($this->never())->method('_stop');
 		$this->Task->execute();
 
-		$result = file_get_contents($path . DS . 'default.pot');
+		$result = file_get_contents($this->path . DS . 'default.pot');
 
 		$pattern = '/msgid "Add User"/';
 		$this->assertPattern($pattern, $result);
diff --git a/cake/tests/cases/console/libs/tasks/fixture.test.php b/cake/tests/cases/console/shells/tasks/fixture.test.php
similarity index 90%
rename from cake/tests/cases/console/libs/tasks/fixture.test.php
rename to cake/tests/cases/console/shells/tasks/fixture.test.php
index e31401e35..7121f7fd1 100644
--- a/cake/tests/cases/console/libs/tasks/fixture.test.php
+++ b/cake/tests/cases/console/shells/tasks/fixture.test.php
@@ -18,20 +18,13 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', array(
+	'tasks/fixture',
+	'tasks/template',
+	'tasks/db_config'
+));
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'fixture.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * FixtureTaskTest class
@@ -56,19 +49,19 @@ class FixtureTaskTest extends CakeTestCase {
  */
 	public function setUp() {
 		parent::setUp();
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock('FixtureTask', 
-			array('in', 'err', 'createFile', '_stop'),
-			array(&$this->Dispatcher)
+			array('in', 'err', 'createFile', '_stop', 'clear'),
+			array($out, $out, $in)
 		);
 		$this->Task->Model = $this->getMock('Shell',
-			array('in', 'out', 'erro', 'createFile', 'getName', 'getTable', 'listAll'),
-			array(&$this->Dispatcher)
+			array('in', 'out', 'error', 'createFile', 'getName', 'getTable', 'listAll'),
+			array($out, $out, $in)
 		);
-		$this->Task->Template =& new TemplateTask($this->Dispatcher);
-		$this->Task->Dispatch->shellPaths = App::path('shells');
+		$this->Task->Template = new TemplateTask($out, $out, $in);
+		$this->Task->DbConfig = $this->getMock('DbConfigTask', array(), array($out, $out, $in));
 		$this->Task->Template->initialize();
 	}
 
@@ -79,7 +72,7 @@ class FixtureTaskTest extends CakeTestCase {
  */
 	public function tearDown() {
 		parent::tearDown();
-		unset($this->Task, $this->Dispatcher);
+		unset($this->Task);
 	}
 
 /**
@@ -88,11 +81,11 @@ class FixtureTaskTest extends CakeTestCase {
  * @return void
  */
 	public function testConstruct() {
-		$this->Dispatcher->params['working'] = DS . 'my' . DS . 'path';
-		$Task = new FixtureTask($this->Dispatcher);
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
 
-		$expected = DS . 'my' . DS . 'path' . DS . 'tests' . DS . 'fixtures' . DS;
-		$this->assertEqual($Task->path, $expected);
+		$Task = new FixtureTask($out, $out, $in);
+		$this->assertEqual($Task->path, APP . 'tests' . DS . 'fixtures' . DS);
 	}
 
 /**
@@ -143,7 +136,7 @@ class FixtureTaskTest extends CakeTestCase {
  *
  * @return void
  */
-	public function testImportRecordsFromDatabaseWithConditions() {
+	public function testImportRecordsFromDatabaseWithConditionsPoo() {
 		$this->Task->interactive = true;
 		$this->Task->expects($this->at(0))->method('in')
 			->will($this->returnValue('WHERE 1=1 LIMIT 10'));
diff --git a/cake/tests/cases/console/libs/tasks/model.test.php b/cake/tests/cases/console/shells/tasks/model.test.php
similarity index 96%
rename from cake/tests/cases/console/libs/tasks/model.test.php
rename to cake/tests/cases/console/shells/tasks/model.test.php
index 105acb6e2..b2072fdeb 100644
--- a/cake/tests/cases/console/libs/tasks/model.test.php
+++ b/cake/tests/cases/console/shells/tasks/model.test.php
@@ -20,21 +20,13 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', array(
+	'tasks/model',
+	'tasks/fixture',
+	'tasks/template'
+));
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'model.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'fixture.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * ModelTaskTest class
@@ -59,12 +51,12 @@ class ModelTaskTest extends CakeTestCase {
  */
 	public function setUp() {
 		parent::setUp();
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock('ModelTask',
 			array('in', 'err', 'createFile', '_stop', '_checkUnitTest'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
 		$this->_setupOtherMocks();
 	}
@@ -75,9 +67,12 @@ class ModelTaskTest extends CakeTestCase {
  * @return void
  */
 	protected function _useMockedOut() {
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock('ModelTask',
 			array('in', 'out', 'err', 'hr', 'createFile', '_stop', '_checkUnitTest'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
 		$this->_setupOtherMocks();
 	}
@@ -88,13 +83,15 @@ class ModelTaskTest extends CakeTestCase {
  * @return void
  */
 	protected function _setupOtherMocks() {
-		$this->Task->Fixture = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher));
-		$this->Task->Test = $this->getMock('FixtureTask', array(), array(&$this->Dispatcher));
-		$this->Task->Template =& new TemplateTask($this->Task->Dispatch);
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
 
-		$this->Task->name = 'ModelTask';
+		$this->Task->Fixture = $this->getMock('FixtureTask', array(), array($out, $out, $in));
+		$this->Task->Test = $this->getMock('FixtureTask', array(), array($out, $out, $in));
+		$this->Task->Template = new TemplateTask($out, $out, $in);
+
+		$this->Task->name = 'Model';
 		$this->Task->interactive = true;
-		$this->Task->Dispatch->shellPaths = App::path('shells');
 	}
 
 /**
@@ -104,7 +101,7 @@ class ModelTaskTest extends CakeTestCase {
  */
 	public function tearDown() {
 		parent::tearDown();
-		unset($this->Task, $this->Dispatcher);
+		unset($this->Task);
 	}
 
 /**
diff --git a/cake/tests/cases/console/shells/tasks/plugin.test.php b/cake/tests/cases/console/shells/tasks/plugin.test.php
new file mode 100644
index 000000000..327b258c2
--- /dev/null
+++ b/cake/tests/cases/console/shells/tasks/plugin.test.php
@@ -0,0 +1,166 @@
+<?php
+/**
+ * PluginTask Test file
+ *
+ * Test Case for plugin generation shell task
+ *
+ * PHP 5
+ *
+ * CakePHP : Rapid Development Framework (http://cakephp.org)
+ * Copyright 2006-2009, Cake Software Foundation, Inc.
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @copyright     Copyright 2006-2009, Cake Software Foundation, Inc.
+ * @link          http://cakephp.org CakePHP Project
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ * @since         CakePHP v 1.3.0
+ * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
+ */
+App::import('Shell', 'Shell', false);
+App::import('Shell', array(
+	'tasks/plugin',
+	'tasks/model'
+));
+
+App::import('Core', array('File'));
+
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
+
+/**
+ * PluginTaskPlugin class
+ *
+ * @package       cake
+ * @subpackage    cake.tests.cases.console.libs.tasks
+ */
+class PluginTaskTest extends CakeTestCase {
+
+/**
+ * setup method
+ *
+ * @return void
+ */
+	public function setUp() {
+		parent::setUp();
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
+		$this->Task = $this->getMock('PluginTask', 
+			array('in', 'err', 'createFile', '_stop', 'clear'),
+			array($out, $out, $in)
+		);
+		$this->Task->path = TMP . 'tests' . DS;
+		
+		$this->_paths = $paths = App::path('plugins');
+		$this->_testPath = array_push($paths, TMP . 'tests' . DS);
+		App::build(array('plugins' => $paths));
+	}
+
+/**
+ * test bake()
+ *
+ * @return void
+ */
+	public function testBakeFoldersAndFiles() {
+		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue($this->_testPath));
+		$this->Task->expects($this->at(1))->method('in')->will($this->returnValue('y'));
+
+		$path = $this->Task->path . 'bake_test_plugin';
+
+		$file = $path . DS . 'bake_test_plugin_app_controller.php';
+		$this->Task->expects($this->at(2))->method('createFile')
+			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
+
+		$file = $path . DS . 'bake_test_plugin_app_model.php';
+		$this->Task->expects($this->at(3))->method('createFile')
+			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
+
+		$this->Task->bake('BakeTestPlugin');
+
+		$path = $this->Task->path . 'bake_test_plugin';
+		$this->assertTrue(is_dir($path), 'No plugin dir %s');
+		
+		$directories = array(
+			'config' . DS . 'schema',
+			'models' . DS . 'behaviors',
+			'models' . DS . 'datasources',
+			'console' . DS . 'shells' . DS . 'tasks',
+			'controllers' . DS . 'components',
+			'libs',
+			'views' . DS . 'helpers',
+			'tests' . DS . 'cases' . DS . 'components',
+			'tests' . DS . 'cases' . DS . 'helpers',
+			'tests' . DS . 'cases' . DS . 'behaviors',
+			'tests' . DS . 'cases' . DS . 'controllers',
+			'tests' . DS . 'cases' . DS . 'models',
+			'tests' . DS . 'groups',
+			'tests' . DS . 'fixtures',
+			'vendors',
+			'webroot'
+		);
+		foreach ($directories as $dir) {
+			$this->assertTrue(is_dir($path . DS . $dir), 'Missing directory for ' . $dir);
+		}
+
+		$Folder = new Folder($this->Task->path . 'bake_test_plugin');
+		$Folder->delete();
+	}
+
+/**
+ * test execute with no args, flowing into interactive,
+ *
+ * @return void
+ */
+	public function testExecuteWithNoArgs() {
+		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('TestPlugin'));
+		$this->Task->expects($this->at(1))->method('in')->will($this->returnValue('3'));
+		$this->Task->expects($this->at(2))->method('in')->will($this->returnValue('y'));
+
+		$path = $this->Task->path . 'test_plugin';
+		$file = $path . DS . 'test_plugin_app_controller.php';
+		$this->Task->expects($this->at(3))->method('createFile')
+			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
+
+		$file = $path . DS . 'test_plugin_app_model.php';
+		$this->Task->expects($this->at(4))->method('createFile')
+			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
+
+		$this->Task->args = array();
+		$this->Task->execute();
+
+		$Folder = new Folder($path);
+		$Folder->delete();
+	}
+
+/**
+ * Test Execute
+ *
+ * @return void
+ */
+	public function testExecuteWithOneArg() {
+		$this->Task->expects($this->at(0))->method('in')
+			->will($this->returnValue($this->_testPath));
+		$this->Task->expects($this->at(1))->method('in')
+			->will($this->returnValue('y'));
+
+		$path = $this->Task->path . 'bake_test_plugin';
+		$file = $path . DS . 'bake_test_plugin_app_controller.php';
+		$this->Task->expects($this->at(2))->method('createFile')
+			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
+
+		$path = $this->Task->path . 'bake_test_plugin';
+		$file = $path . DS . 'bake_test_plugin_app_model.php';
+		$this->Task->expects($this->at(3))->method('createFile')
+			->with($file, new PHPUnit_Framework_Constraint_IsAnything());
+		
+		$this->Task->args = array('BakeTestPlugin');
+
+		$this->Task->execute();
+
+		$Folder = new Folder($this->Task->path . 'bake_test_plugin');
+		$Folder->delete();
+	}
+
+}
diff --git a/cake/tests/cases/console/libs/tasks/project.test.php b/cake/tests/cases/console/shells/tasks/project.test.php
similarity index 57%
rename from cake/tests/cases/console/libs/tasks/project.test.php
rename to cake/tests/cases/console/shells/tasks/project.test.php
index 104102b23..e9044d992 100644
--- a/cake/tests/cases/console/libs/tasks/project.test.php
+++ b/cake/tests/cases/console/shells/tasks/project.test.php
@@ -20,21 +20,11 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'tasks/project');
+
 App::import('Core', 'File');
 
-
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'project.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * ProjectTask Test class
@@ -51,14 +41,13 @@ class ProjectTaskTest extends CakeTestCase {
  */
 	public function setUp() {
 		parent::setUp();
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+		
 		$this->Task = $this->getMock('ProjectTask', 
 			array('in', 'err', 'createFile', '_stop'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
-		$this->Dispatcher->shellPaths = App::path('shells');
 		$this->Task->path = TMP . 'tests' . DS;
 	}
 
@@ -72,7 +61,7 @@ class ProjectTaskTest extends CakeTestCase {
 
 		$Folder = new Folder($this->Task->path . 'bake_test_app');
 		$Folder->delete();
-		unset($this->Dispatcher, $this->Task);
+		unset($this->Task);
 	}
 
 /**
@@ -83,7 +72,6 @@ class ProjectTaskTest extends CakeTestCase {
 	protected function _setupTestProject() {
 		$skel = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS . 'templates' . DS . 'skel';
 		$this->Task->expects($this->at(0))->method('in')->will($this->returnValue('y'));
-		$this->Task->expects($this->at(1))->method('in')->will($this->returnValue('n'));
 		$this->Task->bake($this->Task->path . 'bake_test_app', $skel);
 	}
 
@@ -94,18 +82,32 @@ class ProjectTaskTest extends CakeTestCase {
  */
 	public function testBake() {
 		$this->_setupTestProject();
-
 		$path = $this->Task->path . 'bake_test_app';
+
 		$this->assertTrue(is_dir($path), 'No project dir %s');
-		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
-		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
-		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
-		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
-		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
+		$dirs = array(
+			'config',
+			'config' . DS . 'schema',
+			'console',
+			'console' . DS . 'shells',
+			'console' . DS . 'shells' . DS . 'tasks',
+			'controllers', 
+			'models', 
+			'views',
+			'views' . DS . 'helpers',
+			'tests',
+			'tests' . DS . 'cases', 
+			'tests' . DS . 'cases' . DS . 'models', 
+			'tests' . DS . 'cases', 
+			'tests' . DS . 'fixtures',
+			'tmp',
+			'webroot',
+			'webroot' . DS . 'js',
+			'webroot' . DS . 'css',
+		);
+		foreach ($dirs as $dir) {
+			$this->assertTrue(is_dir($path . DS . $dir), 'Missing ' . $dir);
+		}
 	}
 
 /**
@@ -117,36 +119,24 @@ class ProjectTaskTest extends CakeTestCase {
 		$this->Task->params['empty'] = true;
 		$this->_setupTestProject();
 		$path = $this->Task->path . 'bake_test_app';
-		$this->assertTrue(is_dir($path), 'No project dir %s');
-		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
-		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
-		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
-		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
-		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
-
-		$this->assertTrue(is_file($path . DS . 'controllers' . DS .'components' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'locale' . DS . 'eng' . DS . 'LC_MESSAGES' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'models' . DS . 'behaviors' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'models' . DS . 'datasources' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'plugins' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'behaviors' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'components' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'controllers' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'datasources' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'helpers' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'models' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'cases' . DS . 'shells' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'fixtures' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'tests' . DS . 'groups' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'vendors' . DS . 'shells' . DS . 'tasks' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'views' . DS . 'errors' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'views' . DS . 'helpers' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'views' . DS . 'scaffolds' . DS . 'empty'), 'No empty file in dir %s');
-		$this->assertTrue(is_file($path . DS . 'webroot' . DS . 'js' . DS . 'empty'), 'No empty file in dir %s');
+	
+		$empty = array(
+			'console' . DS . 'shells' . DS . 'tasks',
+			'controllers' . DS . 'components', 
+			'models' . DS . 'behaviors', 
+			'views' . DS . 'helpers',
+			'views' . DS . 'errors',
+			'views' . DS . 'scaffolds',
+			'tests' . DS . 'cases' . DS . 'models', 
+			'tests' . DS . 'cases' . DS . 'controllers',
+			'tests' . DS . 'cases' . DS . 'helpers',
+			'tests' . DS . 'fixtures',
+			'webroot' . DS . 'js'
+		);
+	
+		foreach ($empty as $dir) {
+			$this->assertTrue(is_file($path . DS . $dir . DS . 'empty'), 'Missing empty file in ' . $dir);
+		}
 	}
 
 /**
@@ -275,14 +265,30 @@ class ProjectTaskTest extends CakeTestCase {
 
 		$this->Task->execute();
 		$this->assertTrue(is_dir($path), 'No project dir %s');
-		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir %s');
-		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir %s');
-		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir %s');
-		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir %s');
-		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'groups'), 'No groups dir %s');
-		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir %s');
+		$this->assertTrue(is_dir($path . DS . 'controllers'), 'No controllers dir ');
+		$this->assertTrue(is_dir($path . DS . 'controllers' . DS .'components'), 'No components dir ');
+		$this->assertTrue(is_dir($path . DS . 'models'), 'No models dir');
+		$this->assertTrue(is_dir($path . DS . 'views'), 'No views dir');
+		$this->assertTrue(is_dir($path . DS . 'views' . DS . 'helpers'), 'No helpers dir');
+		$this->assertTrue(is_dir($path . DS . 'tests'), 'No tests dir');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'cases'), 'No cases dir');
+		$this->assertTrue(is_dir($path . DS . 'tests' . DS . 'fixtures'), 'No fixtures dir');
+	}
+
+/**
+ * test console path
+ *
+ * @return void
+ */
+	function testConsolePath() {
+		$this->_setupTestProject();
+		
+		$path = $this->Task->path . 'bake_test_app' . DS;
+		$result = $this->Task->consolePath($path);
+		$this->assertTrue($result);
+
+		$file = new File($path . 'console' . DS . 'cake.php');
+		$contents = $file->read();
+		$this->assertNoPattern('/__CAKE_PATH__/', $contents, 'Console path placeholder left behind.');
 	}
 }
diff --git a/cake/tests/cases/console/libs/tasks/template.test.php b/cake/tests/cases/console/shells/tasks/template.test.php
similarity index 83%
rename from cake/tests/cases/console/libs/tasks/template.test.php
rename to cake/tests/cases/console/shells/tasks/template.test.php
index ec35cf986..9d970a092 100644
--- a/cake/tests/cases/console/libs/tasks/template.test.php
+++ b/cake/tests/cases/console/shells/tasks/template.test.php
@@ -21,19 +21,9 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'tasks/template');
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * TemplateTaskTest class
@@ -49,14 +39,14 @@ class TemplateTaskTest extends CakeTestCase {
  * @return void
  */
 	public function setup() {
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		parent::setUp();
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock('TemplateTask', 
-			array('in', 'err', 'createFile', '_stop'),
-			array(&$this->Dispatcher)
+			array('in', 'err', 'createFile', '_stop', 'clear'),
+			array($out, $out, $in)
 		);
-		$this->Task->Dispatch->shellPaths = App::path('shells');
 	}
 
 /**
@@ -64,10 +54,9 @@ class TemplateTaskTest extends CakeTestCase {
  *
  * @return void
  */
-	public function teardown() {
-		unset($this->Task, $this->Dispatcher);
-		ClassRegistry::flush();
-		App::build();
+	public function tearDown() {
+		parent::tearDown();
+		unset($this->Task);
 	}
 
 /**
@@ -100,7 +89,6 @@ class TemplateTaskTest extends CakeTestCase {
  */
 	public function testFindingInstalledThemesForBake() {
 		$consoleLibs = CAKE_CORE_INCLUDE_PATH . DS . CAKE . 'console' . DS;
-		$this->Task->Dispatch->shellPaths = array($consoleLibs);
 		$this->Task->initialize();
 		$this->assertEqual($this->Task->templatePaths, array('default' => $consoleLibs . 'templates' . DS . 'default' . DS));
 	}
@@ -128,7 +116,7 @@ class TemplateTaskTest extends CakeTestCase {
 		$this->Task->params = array();
 		$result = $this->Task->getThemePath();
 		$this->assertEqual($result, $defaultTheme);
-		$this->assertEqual($this->Dispatcher->params['theme'], 'default');
+		$this->assertEqual($this->Task->params['theme'], 'default');
 	}
 
 /**
@@ -139,7 +127,7 @@ class TemplateTaskTest extends CakeTestCase {
 	public function testGenerate() {
 		App::build(array(
 			'shells' => array(
-				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS .  'test_app' . DS . 'vendors' . DS . 'shells' . DS
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS .  'test_app' . DS . 'console' . DS
 			)
 		));
 		$this->Task->initialize();
@@ -159,7 +147,7 @@ class TemplateTaskTest extends CakeTestCase {
 	public function testGenerateWithTemplateFallbacks() {
 		App::build(array(
 			'shells' => array(
-				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS .  'test_app' . DS . 'vendors' . DS . 'shells' . DS,
+				TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS .  'test_app' . DS . 'console' . DS,
 				CAKE_CORE_INCLUDE_PATH . DS . 'console' . DS
 			)
 		));
diff --git a/cake/tests/cases/console/libs/tasks/test.test.php b/cake/tests/cases/console/shells/tasks/test.test.php
similarity index 94%
rename from cake/tests/cases/console/libs/tasks/test.test.php
rename to cake/tests/cases/console/shells/tasks/test.test.php
index 6d6942ced..d8fa87ead 100644
--- a/cake/tests/cases/console/libs/tasks/test.test.php
+++ b/cake/tests/cases/console/shells/tasks/test.test.php
@@ -20,22 +20,15 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', array(
+	'tasks/test',
+	'tasks/template'
+));
+
 App::import('Controller', 'Controller', false);
 App::import('Model', 'Model', false);
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'test.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * Test Article model
@@ -249,16 +242,15 @@ class TestTaskTest extends CakeTestCase {
  */
 	public function setup() {
 		parent::setup();
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock('TestTask', 
 			array('in', 'err', 'createFile', '_stop', 'isLoadableClass'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
-		$this->Dispatcher->shellPaths = App::path('shells');
-		$this->Task->name = 'TestTask';
-		$this->Task->Template = new TemplateTask($this->Dispatcher);
+		$this->Task->name = 'Test';
+		$this->Task->Template = new TemplateTask($out, $out, $in);
 	}
 
 /**
@@ -266,9 +258,9 @@ class TestTaskTest extends CakeTestCase {
  *
  * @return void
  */
-	public function teardown() {
-		parent::teardown();
-		ClassRegistry::flush();
+	public function tearDown() {
+		parent::tearDown();
+		unset($this->Task);
 	}
 
 /**
@@ -277,8 +269,8 @@ class TestTaskTest extends CakeTestCase {
  * @return void
  */
 	public function testFilePathGenerationModelRepeated() {
-		$this->Dispatcher->expects($this->never())->method('stderr');
-		$this->Dispatcher->expects($this->never())->method('_stop');
+		$this->Task->expects($this->never())->method('err');
+		$this->Task->expects($this->never())->method('_stop');
 
 		$file = TESTS . 'cases' . DS . 'models' . DS . 'my_class.test.php';
 
diff --git a/cake/tests/cases/console/libs/tasks/view.test.php b/cake/tests/cases/console/shells/tasks/view.test.php
similarity index 94%
rename from cake/tests/cases/console/libs/tasks/view.test.php
rename to cake/tests/cases/console/shells/tasks/view.test.php
index 39c1e6e65..5a2dfcce5 100644
--- a/cake/tests/cases/console/libs/tasks/view.test.php
+++ b/cake/tests/cases/console/shells/tasks/view.test.php
@@ -20,23 +20,15 @@
  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  */
 App::import('Shell', 'Shell', false);
+App::import('Shell', array(
+	'tasks/view',
+	'tasks/controller',
+	'tasks/template',
+	'tasks/project',
+	'tasks/db_config'
+));
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
-
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'view.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'controller.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'template.php';
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'tasks' . DS . 'project.php';
-
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
 /**
  * Test View Task Comment Model
@@ -232,18 +224,18 @@ class ViewTaskTest extends CakeTestCase {
  */
 	public function setUp() {
 		parent::setUp();
-		$this->Dispatcher = $this->getMock('ShellDispatcher', array(
-			'getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear'
-		));
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Task = $this->getMock('ViewTask',
 			array('in', 'err', 'createFile', '_stop'),
-			array(&$this->Dispatcher)
+			array($out, $out, $in)
 		);
-		$this->Task->Template = new TemplateTask($this->Dispatcher);
-		$this->Task->Controller = $this->getMock('ControllerTask', array(), array(&$this->Dispatcher));
-		$this->Task->Project = $this->getMock('ProjectTask', array(), array(&$this->Dispatcher));
+		$this->Task->Template = new TemplateTask($out, $out, $in);
+		$this->Task->Controller = $this->getMock('ControllerTask', array(), array($out, $out, $in));
+		$this->Task->Project = $this->getMock('ProjectTask', array(), array($out, $out, $in));
+		$this->Task->DbConfig = $this->getMock('DbConfigTask', array(), array($out, $out, $in));
 
-		$this->Dispatcher->shellPaths = App::path('shells');
 		$this->Task->path = TMP;
 		$this->Task->Template->params['theme'] = 'default';
 	}
@@ -401,7 +393,7 @@ class ViewTaskTest extends CakeTestCase {
 		$this->Task->controllerName = 'ViewTaskComments';
 		$this->Task->controllerPath = 'view_task_comments';
 		$this->Task->plugin = 'TestTest';
-		$this->Task->name = 'ViewTask';
+		$this->Task->name = 'View';
 
 		$path = APP . 'plugins' . DS . 'test_test' . DS . 'views' . DS . 'view_task_comments' . DS  . 'view.ctp';
 		$this->Task->expects($this->once())->method('createFile')
diff --git a/cake/tests/cases/console/libs/testsuite.test.php b/cake/tests/cases/console/shells/testsuite.test.php
similarity index 79%
rename from cake/tests/cases/console/libs/testsuite.test.php
rename to cake/tests/cases/console/shells/testsuite.test.php
index b790e72a6..b132aa557 100644
--- a/cake/tests/cases/console/libs/testsuite.test.php
+++ b/cake/tests/cases/console/shells/testsuite.test.php
@@ -19,19 +19,10 @@
  */
 
 App::import('Shell', 'Shell', false);
+App::import('Shell', 'Testsuite');
 
-if (!defined('DISABLE_AUTO_DISPATCH')) {
-	define('DISABLE_AUTO_DISPATCH', true);
-}
+require_once CAKE . 'console' .  DS . 'shell_dispatcher.php';
 
-if (!class_exists('ShellDispatcher')) {
-	ob_start();
-	$argv = false;
-	require CAKE . 'console' .  DS . 'cake.php';
-	ob_end_clean();
-}
-
-require_once CAKE . 'console' .  DS . 'libs' . DS . 'testsuite.php';
 
 class TestSuiteShellTest extends CakeTestCase {
 
@@ -42,16 +33,14 @@ class TestSuiteShellTest extends CakeTestCase {
  * @return void
  */
 	public function setUp() {
-		$this->Dispatcher = $this->getMock(
-			'ShellDispatcher', 
-			array('getInput', 'stdout', 'stderr', '_stop', '_initEnvironment', 'clear')
-		);
+		$out = $this->getMock('ConsoleOutput', array(), array(), '', false);
+		$in = $this->getMock('ConsoleInput', array(), array(), '', false);
+
 		$this->Shell = $this->getMock(
 			'TestSuiteShell',
-			array('in', 'out', 'hr', 'help', 'error', 'err', '_stop', 'initialize', 'run'),
-			array(&$this->Dispatcher)
+			array('in', 'out', 'hr', 'help', 'error', 'err', '_stop', 'initialize', 'run', 'clear'),
+			array($out, $out, $in)
 		);
-		$this->Shell->Dispatch->shellPaths = App::path('shells');
 	}
 
 /**
@@ -107,12 +96,12 @@ class TestSuiteShellTest extends CakeTestCase {
 	public function testRunnerOptions() {
 		$this->Shell->startup();
 		$this->Shell->args = array('core', 'Basics');
-		$this->Shell->params = array('filter' => 'myFilter', '-colors' => null, '-verbose' => null);
+		$this->Shell->params = array('filter' => 'myFilter', 'colors' => true, 'verbose' => true);
 
 		$this->Shell->expects($this->once())->method('run')
 			->with(
 				array('app' => false, 'plugin' => null, 'output' => 'text', 'case' => 'basics'),
-				array('--colors', '--verbose', '--filter', 'myFilter')
+				array('--filter', 'myFilter', '--colors', '--verbose')
 			);
 		$this->Shell->main();
 	}
diff --git a/cake/tests/cases/libs/all_tests.test.php b/cake/tests/cases/libs/all_tests.test.php
index 6982a1d3c..c3a580103 100644
--- a/cake/tests/cases/libs/all_tests.test.php
+++ b/cake/tests/cases/libs/all_tests.test.php
@@ -38,6 +38,7 @@ class AllTests extends PHPUnit_Framework_TestSuite {
 		$path = CORE_TEST_CASES . DS . 'libs' . DS;
 		$console = CORE_TEST_CASES . DS . 'console' . DS;
 
+		$suite->addTestFile($console . 'all_console_libs.test.php');
 		$suite->addTestFile($console . 'all_shells.test.php');
 		$suite->addTestFile($console . 'all_tasks.test.php');
 
diff --git a/cake/tests/cases/libs/model/cake_schema.test.php b/cake/tests/cases/libs/model/cake_schema.test.php
index b2e056cf1..5750054f5 100644
--- a/cake/tests/cases/libs/model/cake_schema.test.php
+++ b/cake/tests/cases/libs/model/cake_schema.test.php
@@ -969,7 +969,7 @@ class CakeSchemaTest extends CakeTestCase {
 		));
 		$Other =& $this->Schema->load(array('name' => 'TestPluginApp', 'plugin' => 'TestPlugin'));
 		$this->assertEqual($Other->name, 'TestPluginApp');
-		$this->assertEqual(array_keys($Other->tables), array('acos'));
+		$this->assertEqual(array_keys($Other->tables), array('test_plugin_acos'));
 
 		App::build();
 	}
diff --git a/cake/tests/cases/libs/string.test.php b/cake/tests/cases/libs/string.test.php
index ef062956f..1e5e84d54 100644
--- a/cake/tests/cases/libs/string.test.php
+++ b/cake/tests/cases/libs/string.test.php
@@ -307,4 +307,46 @@ class StringTest extends CakeTestCase {
 		$result = String::insert($string, array('b' => 2, 'c' => 3), array('clean' => true));
 		$this->assertEqual($expected, $result);
 	}
+
+/**
+ * test wrap method.
+ *
+ * @return void
+ */
+	function testWrap() {
+		$text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.';
+		$result = String::wrap($text, 33);
+		$expected = <<<TEXT
+This is the song that never ends.
+This is the song that never ends.
+This is the song that never ends.
+TEXT;
+		$this->assertEquals($expected, $result, 'Text not wrapped.');
+
+		$result = String::wrap($text, array('width' => 20, 'wordWrap' => false));
+		$expected = <<<TEXT
+This is the song th
+at never ends. This
+ is the song that n
+ever ends. This is 
+the song that never
+ ends.
+TEXT;
+		$this->assertEquals($expected, $result, 'Text not wrapped.');
+	}
+
+/**
+ * test wrap() indenting
+ *
+ * @return void
+ */
+	function testWrapIndent() {
+		$text = 'This is the song that never ends. This is the song that never ends. This is the song that never ends.';
+		$result = String::wrap($text, array('width' => 33, 'indent' => "\t", 'indentAt' => 1));
+		$expected = <<<TEXT
+This is the song that never ends.
+	This is the song that never ends.
+	This is the song that never ends.
+TEXT;
+	}
 }
diff --git a/cake/tests/test_app/vendors/shells/sample.php b/cake/tests/test_app/console/shells/sample.php
similarity index 100%
rename from cake/tests/test_app/vendors/shells/sample.php
rename to cake/tests/test_app/console/shells/sample.php
diff --git a/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/templates/empty b/cake/tests/test_app/console/shells/tasks/empty
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin_two/vendors/shells/templates/empty
rename to cake/tests/test_app/console/shells/tasks/empty
diff --git a/cake/tests/test_app/vendors/shells/templates/test/classes/test_object.ctp b/cake/tests/test_app/console/templates/test/classes/test_object.ctp
similarity index 100%
rename from cake/tests/test_app/vendors/shells/templates/test/classes/test_object.ctp
rename to cake/tests/test_app/console/templates/test/classes/test_object.ctp
diff --git a/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php b/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php
index e3aa8087d..fd3a283c8 100644
--- a/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php
+++ b/cake/tests/test_app/plugins/test_plugin/config/schema/schema.php
@@ -23,7 +23,7 @@ class TestPluginAppSchema extends CakeSchema {
 
 	public $name = 'TestPluginApp';
 
-	public $acos = array(
+	public $test_plugin_acos = array(
 		'id' => array('type'=>'integer', 'null' => false, 'default' => NULL, 'length' => 10, 'key' => 'primary'),
 		'parent_id' => array('type'=>'integer', 'null' => true, 'default' => NULL, 'length' => 10),
 		'model' => array('type'=>'string', 'null' => true),
diff --git a/cake/tests/test_app/plugins/test_plugin/vendors/shells/example.php b/cake/tests/test_app/plugins/test_plugin/console/shells/example.php
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin/vendors/shells/example.php
rename to cake/tests/test_app/plugins/test_plugin/console/shells/example.php
diff --git a/cake/tests/test_app/vendors/shells/tasks/empty b/cake/tests/test_app/plugins/test_plugin/console/shells/tasks/empty
similarity index 100%
rename from cake/tests/test_app/vendors/shells/tasks/empty
rename to cake/tests/test_app/plugins/test_plugin/console/shells/tasks/empty
diff --git a/cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks/other_task.php b/cake/tests/test_app/plugins/test_plugin/console/shells/tasks/other_task.php
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin/vendors/shells/tasks/other_task.php
rename to cake/tests/test_app/plugins/test_plugin/console/shells/tasks/other_task.php
diff --git a/cake/tests/test_app/plugins/test_plugin/console/templates/empty b/cake/tests/test_app/plugins/test_plugin/console/templates/empty
new file mode 100644
index 000000000..e69de29bb
diff --git a/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/example.php b/cake/tests/test_app/plugins/test_plugin_two/console/shells/example.php
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin_two/vendors/shells/example.php
rename to cake/tests/test_app/plugins/test_plugin_two/console/shells/example.php
diff --git a/cake/tests/test_app/plugins/test_plugin_two/console/shells/tasks/empty b/cake/tests/test_app/plugins/test_plugin_two/console/shells/tasks/empty
new file mode 100644
index 000000000..e69de29bb
diff --git a/cake/tests/test_app/plugins/test_plugin_two/vendors/shells/welcome.php b/cake/tests/test_app/plugins/test_plugin_two/console/shells/welcome.php
similarity index 100%
rename from cake/tests/test_app/plugins/test_plugin_two/vendors/shells/welcome.php
rename to cake/tests/test_app/plugins/test_plugin_two/console/shells/welcome.php
diff --git a/cake/tests/test_app/plugins/test_plugin_two/console/templates/empty b/cake/tests/test_app/plugins/test_plugin_two/console/templates/empty
new file mode 100644
index 000000000..e69de29bb